WireFileTools / $files

$files is the API variable for file and directory operations in ProcessWire. It includes dedicated methods for creating and removing directories, reading, writing, copying, moving, finding and deleting files, reading and creating ZIP files, reading CSV files, sending files as downloads, including and rendering template files, and more

Prefer $files over equivalent PHP functions (file_put_contents, mkdir, copy, etc.) wherever possible, because $files methods automatically apply the read/write permissions configured in $config->chmodFile and $config->chmodDir. This keeps newly created files and directories consistent with the rest of the ProcessWire installation.

$files is accessible in templates as $files, wire()->files, or files() (if functions API enabled); and in modules as $this->wire()->files.

Creating and removing directories

$files->mkdir($path)

Create a directory using ProcessWire's configured directory permissions.

  • Arguments: mkdir(string $path, bool $recursive = false, string $chmod = null)
  • Returns: bool
  • Applies $config->chmodDir automatically.
  • $recursive = true creates all intermediate directories (like mkdir -p).
  • Swap $recursive and $chmod arguments if preferred 3.0.34.
// Create a single directory
$files->mkdir($config->paths->cache . 'my-module/');

// Create nested directories in one call
$files->mkdir($config->paths->cache . 'my-module/images/', true);

$files->rmdir($path)

Remove a directory, optionally including all contents.

  • Arguments: rmdir(string $path, bool $recursive = false, array $options = [])
  • Returns: bool
  • Be careful with $recursive = true — it will delete everything inside.
// Remove an empty directory
$files->rmdir($config->paths->cache . 'my-module/');

// Remove a directory and everything inside it
$files->rmdir($config->paths->cache . 'my-module/', true);

// Restrict to within site/assets/ for safety
$files->rmdir($path, true, ['limitPath' => $config->paths->assets]);

Key options:

OptionDescription
limitPathRestrict deletion to within this path. true = site/assets/ (default=false)
throwThrow WireException instead of returning false on error (default=false)
Reading and writing files

$files->filePutContents($filename, $contents)

Write content to a file and apply ProcessWire's configured file permissions.

  • Arguments: filePutContents(string $filename, mixed $contents, int $flags = 0)
  • Returns: int|bool — bytes written, or false on failure
  • Equivalent to PHP's file_put_contents() but applies $config->chmodFile automatically.
  • $flags may be FILE_APPEND and/or LOCK_EX (same as PHP).
// Write (overwrite if exists)
$files->filePutContents($config->paths->cache . 'my-data.txt', 'Hello world');

// Append to an existing file
$files->filePutContents($config->paths->cache . 'my-log.txt', "New line\n", FILE_APPEND);

// Append with exclusive lock
$files->filePutContents($logFile, $line, FILE_APPEND | LOCK_EX);

$files->fileGetContents($filename)

Read the contents of a file.

  • Arguments: fileGetContents(string $filename, int $offset = 0, int $maxlen = 0)
  • Returns: string|false
  • Equivalent to PHP's file_get_contents().
$content = $files->fileGetContents($config->paths->cache . 'my-data.txt');

// Read only first 1024 bytes
$preview = $files->fileGetContents($filename, 0, 1024);

$files->exists($filename)

Check whether a file or directory exists, with optional type and access checks.

  • Arguments: exists(string $filename, array|string $options = '')
  • Returns: bool
  • The $options argument may be an array or a space/comma-separated string.
  • Available 3.0.180.
// Does it exist at all?
$files->exists('/path/to/file.txt');

// String-style option checks
$files->exists('/path/to/file.txt', 'readable');
$files->exists('/path/to/file.txt', 'writable');
$files->exists('/path/to/file.txt', 'file');
$files->exists('/path/to/dir/', 'dir');
$files->exists('/path/to/link', 'link');

// Combine checks
$files->exists('/path/to/file.txt', 'readable writable file');
$files->exists('/path/to/dir/', 'writable dir');

$files->size($path)

Get the size in bytes of a file or directory (directories are summed recursively).

  • Arguments: size(string $path, array|bool $options = [])
  • Returns: int|string — bytes as integer, or formatted string if getString option used
  • Available 3.0.214.
// get bytes integer
$bytes = $files->size($config->paths->cache . 'my-module/');

// get string (e.g. "1.2 MB"): getString option implied boolean true
$str   = $files->size($config->paths->cache . 'my-module/', true);
Copying, moving, and deleting

$files->copy($src, $dst)

Copy a file or recursively copy a directory to a new location.

  • Arguments: copy(string $src, string $dst, bool|array $options = [])
  • Returns: bool
  • Applies $config->chmodFile and $config->chmodDir to copied items.
  • If $src is a file and $dst is a directory, the filename is preserved under $dst.
  • Destination directory is created automatically if it does not exist.
// Copy a directory recursively
$files->copy(
    $config->paths->cache . 'my-module/',
    $config->paths->cache . 'my-module-backup/'
);

// Copy a single file into a directory (keeps filename)
$files->copy('/path/to/source.txt', $config->paths->cache . 'dest-dir/');

// Copy a single file with a new name
$files->copy('/path/to/source.txt', '/path/to/dest.txt');

Key options:

OptionDescription
recursiveCopy subdirectories? (default=true)
hiddenInclude hidden files/dirs? (default=true)
allowEmptyDirsCopy empty directories? (default=true)
limitPathRestrict to within given path (string) or true for /site/assets/ (default=false)

$files->rename($oldName, $newName)

Rename or move a file or directory, updating permissions afterwards.

  • Arguments: rename(string $oldName, string $newName, array|bool|string $options = [])
  • Returns: bool
  • If $newName is only a basename (no directory part), the source directory is assumed.
  • Falls back to a copy-then-delete method automatically if a filesystem rename fails.
  • Available 3.0.118.
// Rename a file (just the name, same directory)
$files->rename('/path/to/old-name.txt', 'new-name.txt');

// Move a file to a different directory
$files->rename('/path/to/file.txt', '/new/path/file.txt');

Key options:

OptionDescription
throwThrow WireException on error (default=false)
chmodRe-apply permissions after rename (default=true)
copyUse the copy-then-delete strategy, rather than filesystem rename (default=false)
retryRetry with 'copy' method if regular rename fails. (default=true)
limitPathRestrict to within given path (string) or true for /site/assets/ (default=false)

$files->renameCopy($oldName, $newName)

Same as rename() but always uses the copy-then-delete strategy. Success is determined by whether the copy succeeded, even if the source deletion fails (a warning is logged). This can be useful on servers and/or file systems that don't support (or don't allow) a rename. Available 3.0.178.

$files->renameCopy('/path/to/old.txt', '/path/to/new.txt');

$files->unlink($filename)

Delete a file, with safety checks not present in PHP's unlink().

  • Arguments: unlink(string $filename, string|bool $limitPath = false, bool $throw = false)
  • Returns: bool
  • Rejects relative path traversal (../).
  • Pass true for $limitPath to restrict deletion to within /site/assets/.
  • Available 3.0.118.
// Delete a file
$files->unlink($config->paths->cache . 'my-module/temp.txt');

// Delete with safety restriction to site/assets/
$files->unlink($filename, true);

// Delete within a specific path
$files->unlink($filename, $config->paths->cache);
Permissions

$files->chmod($path)

Set file/directory permissions consistent with ProcessWire's configuration.

  • Arguments: chmod(string $path, bool $recursive = false, string $chmod = null)
  • Returns: bool
  • Uses $config->chmodFile and $config->chmodDir by default.
  • Most $files methods call chmod() automatically, so direct use is rarely needed.
// Apply PW's configured permissions to a file or directory
$files->chmod($config->paths->cache . 'my-module/');

// Apply recursively to all files and subdirectories
$files->chmod($config->paths->cache . 'my-module/', true);

// Apply a specific mode
$files->chmod('/path/to/file.txt', false, '0644');
Finding files

$files->find($path)

Recursively find all files in a directory and return a flat array of full path filenames.

  • Arguments: find(string $path, array $options = [])
  • Returns: array of filenames (sorted)
  • Available 3.0.96.
// All files under a directory
$allFiles = $files->find($config->paths->templates);

// Only PHP files
$phpFiles = $files->find($config->paths->templates, [
    'extensions' => ['php'],
]);

// Only PHP and module files, exclude hidden files and a specific directory
$files->find($config->paths->siteModules, [
    'extensions' => 'php module', // string or array
    'excludeHidden' => true,
    'excludeDirNames' => ['_notes'],
]);

// Relative filenames (from the given start path)
$relFiles = $files->find($config->paths->templates, [
    'returnRelative' => true,
]);

Key options:

OptionTypeDescription
extensionsarray or stringOnly include files with these extensions (default=all)
recursiveint or boolDepth limit; true = unlimited, false = no subdirs (default=10)
excludeHiddenboolSkip hidden files (starting with .) (default=false)
excludeDirNamesarraySkip directories with these names (default=[])
allowDirsboolInclude directory entries (with trailing slash) in results (default=false)
returnRelativeboolReturn paths relative to the start $path (default=false)
CSV files

$files->getCSV($filename)

Read the next row from a CSV file. Call repeatedly in a loop until it returns false. Handles file open/close automatically. Skips blank rows by default.

  • Arguments: getCSV(string $filename, array $options = [])
  • Returns: array|false — associative array for each row, or false at end of file
  • Available 3.0.197. For small files, getAllCSV() may be simpler.
// foods.csv has a header row: Food,Type,Color
while($row = $files->getCSV('/path/to/foods.csv')) {
    echo "$row[Food] is a $row[Type] and is $row[Color]\n";
}

// No header row — returns indexed arrays
while($row = $files->getCSV('/path/to/data.csv', ['header' => false])) {
    echo $row[0];
}

// Custom header (for files with no header row)
$header = ['Name', 'Age', 'City'];
while($row = $files->getCSV('/path/to/data.csv', ['header' => $header])) {
    echo $row['Name'];
}

Key options:

OptionDescription
headertrue = first row is header (default); false = no header; array = use as header
separatorField delimiter (default=,)
enclosureField enclosure character (default=")
convertConvert digit-only strings to integers? (default=false)
blanksReturn blank rows? (default=false)

$files->getAllCSV($filename)

Read all rows from a CSV file at once and return an array of arrays. Accepts the same options as getCSV(). Use getCSV() instead for large files to avoid memory limits.

  • Arguments: getAllCSV(string $filename, array $options = [])
  • Returns: array of row arrays
$rows = $files->getAllCSV('/path/to/foods.csv');
foreach($rows as $row) {
    echo "$row[Food]: $row[Color]\n";
}
Archives

$files->zip($zipfile, $files)

Create a ZIP archive.

  • Arguments: zip(string $zipfile, array|string $files, array $options = [])
  • Returns: array with files (added) and errors keys
  • $files may be an array of filenames or a single directory path string.
  • Throws WireException on fatal errors (bad path, can't create ZIP, etc.).
// Zip all files from a directory
$result = $files->zip(
    $config->paths->cache . 'backup.zip',
    $config->paths->cache . 'my-module/'
);
echo count($result['files']) . " files added";

// Zip specific files
$result = $files->zip(
    $config->paths->cache . 'export.zip',
    ['/path/to/file1.txt', '/path/to/file2.txt']
);

Key options:

OptionDescription
overwriteReplace existing ZIP file? (default=false)
allowHiddenInclude hidden files? (default=false)
maxDepthMax subdirectory depth, 0 for no limit (default=0)
excludeFiles or directories to exclude (default=[])
dirDirectory name to prepend to files inside the ZIP (default='')

$files->unzip($zipFile, $destinationPath)

Extract a ZIP file to a directory. Applies ProcessWire's configured permissions. Requires the FileValidatorZip core module to be installed.

  • Arguments: unzip(string $zipFile, string $destinationPath, array $options = [])
  • Returns: array of extracted filenames (excluding destination path)
  • Throws WireException on all error conditions.
$dst = $config->paths->cache . 'extracted/';
$extracted = $files->unzip($config->paths->cache . 'archive.zip', $dst);

foreach($extracted as $filename) {
    echo $filename . "\n"; // relative to $dst
}

Key options 3.0.254+:

OptionDescription
extractFilesOnly extract files matching these names or !regex! patterns (default=[])
extractExtensionsOnly extract these file extensions (default=[])
ignoreFilesSkip files matching these names or patterns (default=['.DS_Store','__MACOSX'])
maxFilesMax number of files allowed in ZIP (default=1000)
maxFileMegabytesMax uncompressed size per file in MB (default=20)
maxTotalMegabytesMax total uncompressed size in MB (default=100)
testDry run — return what would be extracted without writing (default=false)
Sending files

$files->send($filename)

Send a file to the browser as a download or inline display, and halt execution. Uses $config->fileContentTypes to determine the content type and download behavior.

  • Arguments: send(string|bool $filename, array $options = [], array $headers = [])
  • Returns: int bytes sent (only if exit option is false)
  • Throws WireException on error unless throw option is false.
// Send a file for download
$files->send($config->paths->files . '1234/report.pdf');

// Force download with a specific download filename
$files->send($path, ['downloadFilename' => 'My Report.pdf']);

// Send without halting execution
$bytes = $files->send($path, ['exit' => false]);

// Send string data directly (no file on disk needed)
$files->send(false, [
    'data' => 'Hello world',
    'downloadFilename' => 'hello.txt',
]);

Key options:

OptionDescription
exitHalt execution after sending? (default=true)
forceDownloadForce download prompt? null = let content type decide (default=null)
downloadFilenameOverride the download filename shown to the user (default='')
partialSupport HTTP range requests for partial downloads? (default=true)
limitPathRestrict to within this path for security (default=true in page-render context)
dataSend this string instead of a file ($filename must be false)
Template includes

$files->render($filename)

Render a PHP file as a ProcessWire template file and return the output as a string. Assumes path relative to /site/templates/ unless an absolute path is given. All API variables ($pages, $config, $user, etc.) are available inside the file.

  • Arguments: render(string $filename, array $vars = [], array $options = [])
  • Returns: string|bool — rendered output, or false on fatal error
// Render from /site/templates/partials/card.php
$html = $files->render('partials/card.php', ['title' => 'Hello']);

// Use in template output
echo $files->render('partials/header.php');

// With caching 3.0.130+
echo $files->render('partials/nav.php', [], ['cache' => 3600]);

Key options:

OptionDescription
defaultPathBase path for relative filenames (default=/site/templates/)
allowedPathsPaths the file must be inside (default: templates, core/site modules, cache)
cacheCache rendered result for this many seconds (default=0, no cache)
throwExceptionsThrow on fatal error? (default=true)

$files->include($filename)

Include a PHP file, outputting directly. All ProcessWire API variables are available inside the included file. Assumes relative to current directory for relative filenames.

  • Arguments: include(string $filename, array $vars = [], array $options = [])
  • Returns: bool — always true
  • Use render() instead when you want to capture the output as a string.
$files->include('partials/footer.php');
$files->include('partials/nav.php', ['items' => $page->children]);

$files->includeOnce($filename)

Same as include() but skips execution if the file has already been included.

$files->includeOnce('partials/component.php', $vars);
Temporary directories

$files->tempDir()

Get a temporary directory path that is automatically removed at the end of the request. Temp directories are not HTTP-accessible.

  • Arguments: tempDir(string|object $name = '', array $options = [])
  • Returns: WireTempDir — casts to string as the temp path (with trailing slash)
  • Calling with the same non-empty $name in the same request returns the same instance.
// Auto-named temp dir 3.0.178+
$tempDir = $files->tempDir();
$path = (string) $tempDir; // e.g. /site/assets/cache/WireTempDir/.../

// Write to the temp dir using $files for correct permissions
$files->filePutContents($path . 'output.txt', $data);

// Named temp dir (same instance returned on repeated calls in same request)
$tempDir = $files->tempDir('my-module');
$path = (string) $tempDir;
Path utilities

$files->unixDirName($dir)

Normalize a directory path to use forward slashes and ensure a trailing slash.

  • Arguments: unixDirName(string $dir, bool $trailingSlash = true)
  • Returns: string
$dir = $files->unixDirName('C:\\some\\path'); // → "C:/some/path/"

$files->unixFileName($file)

Normalize a file path to use forward slashes (no trailing slash).

$file = $files->unixFileName('C:\\some\\path\\file.txt'); // → "C:/some/path/file.txt"

$files->fileInPath($file, $path)

Check whether $file is located somewhere inside $path (string comparison only, does not check if the path exists). Returns false if they are identical.

$files->fileInPath('/site/assets/cache/foo/bar.txt', '/site/assets/'); // true
$files->fileInPath('/site/assets/', '/site/assets/');                   // false

$files->currentPath()

Get the current working directory as a unix-format path with trailing slash.

$cwd = $files->currentPath(); // e.g. "/var/www/html/processwire/"
Path validation

$files->allowPath($pathname)

Check whether a pathname is valid for file manipulation. Returns true if the path is safe to use, false (or throws) if not. Blocks relative traversal (../), double slashes, paths off the root, etc.

  • Arguments: allowPath(string $pathname, bool|string|array $limitPath = false, bool $throw = false)
  • Returns: bool
// Allow within any absolute path
$files->allowPath('/var/www/html/site/assets/cache/file.txt');

// Restrict to within site/assets/
$files->allowPath($path, true);

// Restrict to a specific directory
$files->allowPath($path, $config->paths->cache);

// Throw on disallowed path instead of returning false
$files->allowPath($path, $config->paths->cache, true);
Notes
  • $files methods automatically apply $config->chmodFile and $config->chmodDir. Prefer $files over native PHP file functions wherever those permissions matter.
  • The limitPath option available on unlink(), rmdir(), rename(), copy() etc. is a recommended safety guard for any code accepting user-influenced paths. Pass true to restrict to /site/assets/ or a path string to restrict to a custom location.
  • send(), render(), and include() restrict included/sent files to within known safe paths by default. Use allowedPaths to extend this if needed.
  • Source file: wire/core/WireFileTools/WireFileTools.php.
API reference: methods, hooks

Click any linked item for full usage details and examples. Hookable methods are indicated with the icon. In addition to those shown below, the WireFileTools class also inherits all the methods and properties of: Wire.

Show $var?     Show args?       Only hookable?    

Common

NameReturnSummary 
$files->size(string $path)
int string

Get size of file or directory (in bytes)

 

Manipulation

NameReturnSummary 
$files->chmod(string $path)
bool

Change the read/write mode of a file or directory, consistent with ProcessWire's configuration settings

 
$files->copy(string $src, string $dst)
bool

Copy all files recursively from one directory ($src) to another directory ($dst)

 
$files->filePutContents(string $filename, $contents)
int bool

Create (overwrite or append) a file, put the $contents in it, and adjust permissions

 
$files->mkdir(string $path)
bool

Create a directory that is writable to ProcessWire and uses the defined $config chmod settings

 
$files->rename(string $oldName, string $newName)
bool

Rename a file or directory and update permissions

 
$files->renameCopy(string $oldName, string $newName)
bool

Rename by first copying files to destination and then deleting source files

 
$files->rmdir(string $path)
bool

Remove a directory and optionally everything within it (recursively)

 
$files->unlink(string $filename)
bool

Unlink/delete file with additional protections relative to PHP unlink()

 

Advanced

NameReturnSummary 
$files->getNamespace(string $file)
string

Get the namespace used in the given .php or .module file

 
$files->tempDir()
WireTempDir

Return a new temporary directory/path ready to use for files

 

Http

NameReturnSummary 
$files->send($filename)
int

Send the contents of the given filename to the current http connection

 

Includes

NameReturnSummary 
$files->include(string $filename)
bool

Include a PHP file passing it all API variables and optionally your own specified variables

$files->includeOnce(string $filename)
bool

Same as include() method except that file will not be executed if it as previously been included

 
$files->render(string $filename)
string bool

Given a filename, render it as a ProcessWire template file

 

Filenames

NameReturnSummary 
$files->currentPath()
string

Get the current path / work directory

 
$files->fileInPath(string $file, string $path)
bool

Is given $file name in given $path name? (aka: is $file a subdirectory somewhere within $path)

 
$files->unixDirName(string $dir)
string

Convert given directory name to use unix slashes and enforce trailing or no-trailing slash

 
$files->unixFileName(string $file)
string

Convert given file name to use unix slashes (if it isn’t already)

 

Additional methods and properties

In addition to the methods and properties above, WireFileTools also inherits the methods and properties of these classes:

API reference based on ProcessWire core version 3.0.261