.
*/
require_once "phing/Task.php";
require_once 'phing/system/io/PhingFile.php';
/**
* ManifestTask
*
* Generates a simple Manifest file with optional checksums.
*
*
* Manifest schema:
* ...
* path/to/file CHECKSUM [CHECKSUM2] [CHECKSUM3]
* path/to/secondfile CHECKSUM [CHECKSUM2] [CHECKSUM3]
* ...
*
* Example usage:
*
*
*
*
*
*
*
*
*
*
* @author David Persson
* @package phing.tasks.ext
* @version $Id: ManifestTask.php 905 2010-10-05 16:28:03Z mrook $
* @since 2.3.1
*/
class ManifestTask extends Task
{
var $taskname = 'manifest';
/**
* Action
*
* "w" for reading in files from fileSet
* and writing manifest
*
* or
*
* "r" for reading in files from fileSet
* and checking against manifest
*
* @var string "r" or "w"
*/
private $action = 'w';
/**
* The target file passed in the buildfile.
*/
private $destFile = null;
/**
* Holds filesets
*
* @var array An Array of objects
*/
private $filesets = array();
/**
* Enable/Disable checksuming or/and select algorithm
* true defaults to md5
* false disables checksuming
* string "md5,sha256,..." enables generation of multiple checksums
* string "sha256" generates sha256 checksum only
*
* @var mixed
*/
private $checksum = false;
/**
* A string used in hashing method
*
* @var string
*/
private $salt = '';
/**
* Holds some data collected during runtime
*
* @var array
*/
private $meta = array('totalFileCount' => 0,'totalFileSize' => 0);
/**
* The setter for the attribute "file"
* This is where the manifest will be written to/read from
*
* @param string Path to readable file
* @return void
*/
public function setFile(PhingFile $file)
{
$this->file = $file;
}
/**
* The setter for the attribute "checksum"
*
* @param mixed $mixed
* @return void
*/
public function setChecksum($mixed)
{
if(is_string($mixed)) {
$data = array(strtolower($mixed));
if(strpos($data[0],',')) {
$data = explode(',',$mixed);
}
$this->checksum = $data;
} elseif($mixed === true) {
$this->checksum = array('md5');
}
}
/**
* The setter for the optional attribute "salt"
*
* @param string $string
* @return void
*/
public function setSalt($string)
{
$this->salt = $string;
}
/**
* Nested creator, creates a FileSet for this task
*
* @access public
* @return object The created fileset object
*/
public function createFileSet()
{
$num = array_push($this->filesets, new FileSet());
return $this->filesets[$num-1];
}
/**
* The init method: Do init steps.
*/
public function init()
{
// nothing to do here
}
/**
* Delegate the work
*/
public function main()
{
$this->validateAttributes();
if($this->action == 'w') {
$this->write();
} elseif($this->action == 'r') {
$this->read();
}
}
/**
* Creates Manifest file
* Writes to $this->file
*
* @throws BuildException
*/
private function write()
{
$project = $this->getProject();
if(!touch($this->file->getPath())) {
throw new BuildException("Unable to write to ".$this->file->getPath().".");
}
$this->log("Writing to " . $this->file->__toString(), Project::MSG_INFO);
if(is_array($this->checksum)) {
$this->log("Using " . implode(', ',$this->checksum)." for checksuming.", Project::MSG_INFO);
}
foreach($this->filesets as $fs) {
$dir = $fs->getDir($this->project)->getPath();
$ds = $fs->getDirectoryScanner($project);
$fromDir = $fs->getDir($project);
$srcFiles = $ds->getIncludedFiles();
$srcDirs = $ds->getIncludedDirectories();
foreach($ds->getIncludedFiles() as $file_path) {
$line = $file_path;
if($this->checksum) {
foreach($this->checksum as $algo) {
if(!$hash = $this->hashFile($dir.'/'.$file_path,$algo)) {
throw new BuildException("Hashing $dir/$file_path with $algo failed!");
}
$line .= "\t".$hash;
}
}
$line .= "\n";
$manifest[] = $line;
$this->log("Adding file ".$file_path,Project::MSG_VERBOSE);
$this->meta['totalFileCount'] ++;
$this->meta['totalFileSize'] += filesize($dir.'/'.$file_path);
}
}
file_put_contents($this->file,$manifest);
$this->log("Done. Total files: ".$this->meta['totalFileCount'].". Total file size: ".$this->meta['totalFileSize']." bytes.", Project::MSG_INFO);
}
/**
* @todo implement
*/
private function read()
{
throw new BuildException("Checking against manifest not yet supported.");
}
/**
* Wrapper method for hash generation
* Automatically selects extension
* Falls back to built-in functions
*
* @link http://www.php.net/mhash
* @link http://www.php.net/hash
*
* @param string $msg The string that should be hashed
* @param string $algo Algorithm
* @return mixed String on success, false if $algo is not available
*/
private function hash($msg,$algo)
{
if(extension_loaded('hash')) {
$algo = strtolower($algo);
if(in_array($algo,hash_algos())) {
return hash($algo,$this->salt.$msg);
}
}
if(extension_loaded('mhash')) {
$algo = strtoupper($algo);
if(defined('MHASH_'.$algo)) {
return mhash('MHASH_'.$algo,$this->salt.$msg);
}
}
switch(strtolower($algo)) {
case 'md5':
return md5($this->salt.$msg);
case 'crc32':
return abs(crc32($this->salt.$msg));
}
return false;
}
/**
* Hash a files contents
* plus it's size an modification time
*
* @param string $file
* @param string $algo
* @return mixed String on success, false if $algo is not available
*/
private function hashFile($file,$algo)
{
if(!file_exists($file)) {
return false;
}
$msg = file_get_contents($file).filesize($file).filemtime($file);
return $this->hash($msg,$algo);
}
/**
* Validates attributes coming in from XML
*
* @access private
* @return void
* @throws BuildException
*/
protected function validateAttributes()
{
if($this->action != 'r' && $this->action != 'w') {
throw new BuildException("'action' attribute has non valid value. Use 'r' or 'w'");
}
if(empty($this->salt)) {
$this->log("No salt provided. Specify one with the 'salt' attribute.", Project::MSG_WARN);
}
if (is_null($this->file) && count($this->filesets) === 0) {
throw new BuildException("Specify at least sources and destination - a file or a fileset.");
}
if (!is_null($this->file) && $this->file->exists() && $this->file->isDirectory()) {
throw new BuildException("Destination file cannot be a directory.");
}
}
}