Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/**
3
 * $Id: ManifestTask.php 341 2008-01-21 14:41:07Z mrook $
4
 *
5
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
6
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
7
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
8
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
9
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
10
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
11
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
12
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
13
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
15
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16
 *
17
 * This software consists of voluntary contributions made by many individuals
18
 * and is licensed under the LGPL. For more information please see
19
 * <http://phing.info>.
20
 */
21
 
22
require_once "phing/Task.php";
23
require_once 'phing/system/io/PhingFile.php';
24
 
25
/**
26
 * ManifestTask
27
 *
28
 * Generates a simple Manifest file with optional checksums.
29
 *
30
 *
31
 * Manifest schema:
32
 * ...
33
 * path/to/file		CHECKSUM	[CHECKSUM2]		[CHECKSUM3]
34
 * path/to/secondfile		CHECKSUM	[CHECKSUM2]		[CHECKSUM3]
35
 * ...
36
 *
37
 * Example usage:
38
 * <manifest checksum="crc32" file="${dir_build}/Manifest">
39
 * 		<fileset refid="files_build" />
40
 * </manifest>
41
 *
42
 * <manifest checksum="md5,adler32,sha256" file="${dir_build}/Manifest">
43
 * 		<fileset refid="files_build" />
44
 * </manifest>
45
 *
46
 *
47
 *
48
 * @author David Persson <davidpersson at qeweurope dot org>
49
 * @since 2.3.1
50
 */
51
class ManifestTask extends Task
52
{
53
	var $taskname = 'manifest';
54
 
55
	/**
56
	 * Action
57
	 *
58
	 * "w" for reading in files from fileSet
59
	 * and writing manifest
60
	 *
61
	 * or
62
	 *
63
	 * "r" for reading in files from fileSet
64
	 * and checking against manifest
65
	 *
66
	 * @var string "r" or "w"
67
	 */
68
	private $action = 'w';
69
 
70
    /**
71
     * The target file passed in the buildfile.
72
     */
73
    private $destFile = null;
74
 
75
    /**
76
     * Holds filesets
77
     *
78
     * @var array An Array of objects
79
     */
80
	private $filesets = array();
81
 
82
    /**
83
     * Enable/Disable checksuming or/and select algorithm
84
     * true defaults to md5
85
     * false disables checksuming
86
     * string "md5,sha256,..." enables generation of multiple checksums
87
     * string "sha256" generates sha256 checksum only
88
     *
89
     * @var mixed
90
     */
91
	private $checksum = false;
92
 
93
	/**
94
	 * A string used in hashing method
95
	 *
96
	 * @var string
97
	 */
98
	private $salt = '';
99
 
100
	/**
101
	 * Holds some data collected during runtime
102
	 *
103
	 * @var array
104
	 */
105
	private $meta = array('totalFileCount' => 0,'totalFileSize' => 0);
106
 
107
 
108
    /**
109
     * The setter for the attribute "file"
110
     * This is where the manifest will be written to/read from
111
     *
112
     * @param string Path to readable file
113
     * @return void
114
     */
115
    public function setFile(PhingFile $file)
116
    {
117
        $this->file = $file;
118
    }
119
 
120
    /**
121
     * The setter for the attribute "checksum"
122
     *
123
     * @param mixed $mixed
124
     * @return void
125
     */
126
    public function setChecksum($mixed)
127
    {
128
    	if(is_string($mixed)) {
129
    		$data = array(strtolower($mixed));
130
 
131
    		if(strpos($data[0],',')) {
132
				$data = explode(',',$mixed);
133
			}
134
 
135
			$this->checksum = $data;
136
 
137
    	} elseif($mixed === true) {
138
	        $this->checksum = array('md5');
139
 
140
    	}
141
    }
142
 
143
    /**
144
     * The setter for the optional attribute "salt"
145
     *
146
     * @param string $string
147
     * @return void
148
     */
149
    public function setSalt($string)
150
    {
151
    	$this->salt = $string;
152
    }
153
 
154
    /**
155
     * Nested creator, creates a FileSet for this task
156
     *
157
     * @access  public
158
     * @return  object  The created fileset object
159
     */
160
    public function createFileSet()
161
    {
162
        $num = array_push($this->filesets, new FileSet());
163
        return $this->filesets[$num-1];
164
    }
165
 
166
    /**
167
     * The init method: Do init steps.
168
     */
169
    public function init()
170
    {
171
      // nothing to do here
172
    }
173
 
174
    /**
175
     * Delegate the work
176
     */
177
    public function main()
178
    {
179
		$this->validateAttributes();
180
 
181
		if($this->action == 'w') {
182
			$this->write();
183
 
184
		} elseif($this->action == 'r') {
185
			$this->read();
186
 
187
		}
188
    }
189
 
190
    /**
191
     * Creates Manifest file
192
     * Writes to $this->file
193
     *
194
     * @throws BuildException
195
     */
196
    private function write()
197
    {
198
      	$project = $this->getProject();
199
 
200
    	if(!touch($this->file->getPath())) {
201
			throw new BuildException("Unable to write to ".$this->file->getPath().".");
202
		}
203
 
204
		$this->log("Writing to " . $this->file->__toString(), Project::MSG_INFO);
205
 
206
		if(is_array($this->checksum)) {
207
			$this->log("Using " . implode(', ',$this->checksum)." for checksuming.", Project::MSG_INFO);
208
		}
209
 
210
		foreach($this->filesets as $fs) {
211
 
212
			$dir = $fs->getDir($this->project)->getPath();
213
 
214
            $ds = $fs->getDirectoryScanner($project);
215
            $fromDir  = $fs->getDir($project);
216
            $srcFiles = $ds->getIncludedFiles();
217
            $srcDirs  = $ds->getIncludedDirectories();
218
 
219
            foreach($ds->getIncludedFiles() as $file_path) {
220
				$line = $file_path;
221
				if($this->checksum) {
222
					foreach($this->checksum as $algo) {
223
						if(!$hash = $this->hashFile($dir.'/'.$file_path,$algo)) {
224
							throw new BuildException("Hashing $dir/$file_path with $algo failed!");
225
						}
226
 
227
						$line .= "\t".$hash;
228
					}
229
				}
230
				$line .= "\n";
231
				$manifest[] = $line;
232
				$this->log("Adding file ".$file_path,Project::MSG_VERBOSE);
233
				$this->meta['totalFileCount'] ++;
234
				$this->meta['totalFileSize'] += filesize($dir.'/'.$file_path);
235
			}
236
 
237
		}
238
 
239
		file_put_contents($this->file,$manifest);
240
 
241
		$this->log("Done. Total files: ".$this->meta['totalFileCount'].". Total file size: ".$this->meta['totalFileSize']." bytes.", Project::MSG_INFO);
242
    }
243
 
244
    /**
245
     * @todo implement
246
     */
247
    private function read()
248
    {
249
    	throw new BuildException("Checking against manifest not yet supported.");
250
    }
251
 
252
    /**
253
     * Wrapper method for hash generation
254
     * Automatically selects extension
255
     * Falls back to built-in functions
256
     *
257
     * @link  http://www.php.net/mhash
258
     * @link  http://www.php.net/hash
259
     *
260
     * @param string $msg The string that should be hashed
261
     * @param string $algo Algorithm
262
     * @return mixed String on success, false if $algo is not available
263
     */
264
    private function hash($msg,$algo)
265
    {
266
		if(extension_loaded('hash')) {
267
			$algo = strtolower($algo);
268
 
269
			if(in_array($algo,hash_algos())) {
270
				return hash($algo,$this->salt.$msg);
271
			}
272
 
273
    	}
274
 
275
    	if(extension_loaded('mhash')) {
276
			$algo = strtoupper($algo);
277
 
278
			if(defined('MHASH_'.$algo)) {
279
				return mhash('MHASH_'.$algo,$this->salt.$msg);
280
 
281
			}
282
		}
283
 
284
		switch(strtolower($algo)) {
285
			case 'md5':
286
				return md5($this->salt.$msg);
287
			case 'crc32':
288
				return abs(crc32($this->salt.$msg));
289
		}
290
 
291
		return false;
292
    }
293
 
294
    /**
295
     * Hash a files contents
296
     * plus it's size an modification time
297
     *
298
     * @param string $file
299
     * @param string $algo
300
     * @return mixed String on success, false if $algo is not available
301
     */
302
    private function hashFile($file,$algo)
303
    {
304
    	if(!file_exists($file)) {
305
    		return false;
306
    	}
307
 
308
    	$msg = file_get_contents($file).filesize($file).filemtime($file);
309
 
310
    	return $this->hash($msg,$algo);
311
    }
312
 
313
    /**
314
     * Validates attributes coming in from XML
315
     *
316
     * @access  private
317
     * @return  void
318
     * @throws  BuildException
319
     */
320
    protected function validateAttributes()
321
    {
322
        if($this->action != 'r' && $this->action != 'w') {
323
    		throw new BuildException("'action' attribute has non valid value. Use 'r' or 'w'");
324
    	}
325
 
326
    	if(empty($this->salt)) {
327
    		$this->log("No salt provided. Specify one with the 'salt' attribute.", Project::MSG_WARN);
328
    	}
329
 
330
        if (is_null($this->file) && count($this->filesets) === 0) {
331
            throw new BuildException("Specify at least sources and destination - a file or a fileset.");
332
        }
333
 
334
        if (!is_null($this->file) && $this->file->exists() && $this->file->isDirectory()) {
335
            throw new BuildException("Destination file cannot be a directory.");
336
        }
337
 
338
    }
339
}
340
 
341