SizeRestrictedPackageCache::getPackageFilename()   D
last analyzed

Complexity

Conditions 9
Paths 24

Size

Total Lines 41
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 41
rs 4.909
c 0
b 0
f 0
cc 9
eloc 24
nc 24
nop 5
1
<?php
2
3
/**
4
 * Class for calling the package generator and caching the results.
5
 */
6
class SizeRestrictedPackageCache implements PackageCache {
7
8
	protected $cacheSize;
9
	protected $baseDir;
10
11
	/**
12
	 * Set the maximum number of items that will be stored in the package cache.
13
	 *
14
	 * If null, items will never be deleted. If set, cache entries will be touched whenever they are accessed,
15
	 * and the least-recently-access items will be deleted.
16
	 *
17
	 * @param int $cacheSize the number of package files to keep.
18
	 */
19
	public function setCacheSize($cacheSize) {
20
		$this->cacheSize = $cacheSize;
21
	}
22
23
	/**
24
	 * The base directory to store cached packages in.
25
	 * The files will be stored in a subdirectory named after the identifier.
26
	 * If base directory isn't set, the getPackageFilename() won't run.
27
	 *
28
	 * @param string $baseDir The base directory
29
	 */
30
	public function setBaseDir($baseDir) {
31
		$this->baseDir = realpath($baseDir);
32
	}
33
34
	/**
35
	 * Return the filename of the generated package, retrieving from cache or generating as necessary
36
	 *
37
	 * @param \PackageGenerator $generator The generator to use to create cache entries.
38
	 * @param string $identifier A unique identifier for the generator; used to partition the cache
39
	 * @param string $sha The SHA of the commit to be deployed
40
	 * @param string $repositoryDir The directory where the repository resides
41
	 * @param \DeploynautLogFile $log The log to write status output to, including package-generation commands
42
	 *
43
	 * @return string
44
	 */
45
	public function getPackageFilename(
46
		\PackageGenerator $generator,
47
		$identifier, $sha,
48
		$repositoryDir,
49
		\DeploynautLogFile $log
50
	) {
51
		if(!$this->baseDir) {
52
			throw new \LogicException("Can't use PackageCache without setting BaseDir");
53
		}
54
55
		$buildPath = $this->baseDir . '/' . $this->sanitiseDirName($identifier);
56
		$filename = "$buildPath/$sha.tar.gz";
57
58
		if(!file_exists($this->baseDir)) {
59
			if(!mkdir($this->baseDir)) {
60
				throw new \LogicException("Can't create base dir {$this->baseDir}");
61
			}
62
		}
63
		if(!file_exists($buildPath)) {
64
			if(!mkdir($buildPath)) {
65
				throw new \LogicException("Can't create build path $buildPath");
66
			}
67
		}
68
69
		if(file_exists($filename)) {
70
			$log->write("Using previously generated package $filename");
71
			// This will ensure that our cache garbage collection will remove least-recently-accessed,
72
			// rather than oldest.
73
			touch($filename);
74
			return $filename;
75
76
		} else {
77
			if($this->cacheSize) {
78
				$this->reduceDirSizeTo($buildPath, $this->cacheSize - 1, $log);
79
			}
80
81
			if($generator->generatePackage($sha, $repositoryDir, $filename, $log)) {
82
				return $filename;
83
			}
84
		}
85
	}
86
87
	/**
88
	 * Take the identifier an make it safe to use as a directory name.
89
	 *
90
	 * @param string $identifier The unsanitised directory name.
91
	 */
92
	protected function sanitiseDirName($identifier) {
93
		$safe = preg_replace('/[^A-Za-z0-9_-]/', '', $identifier);
94
		return $safe ? $safe : 'null';
95
	}
96
97
	/**
98
	 * Delete items in this directory until the number of items is <= $count.
99
	 * Delete the oldest files first.
100
	 *
101
	 * @param string $dir The directory to remove items from
102
	 * @param int $count The maximum number of .tar.gz files that can appear in that directory
103
	 * @param \DeploynautLogFile $log The log to send removal status messages to
104
	 */
105
	protected function reduceDirSizeTo($dir, $count, \DeploynautLogFile $log) {
106
		$files = glob($dir . '/*.tar.gz');
107
		if(sizeof($files) > $count) {
108
			usort($files, function($a, $b) {
109
				return filemtime($a) > filemtime($b);
110
			});
111
112
			for($i = 0; $i < sizeof($files) - $count; $i++) {
113
				$log->write("Removing " . $files[$i] . " from package cache");
114
				unlink($files[$i]);
115
			}
116
		}
117
	}
118
}
119