Completed
Pull Request — master (#715)
by Andrew
06:13
created

SizeRestrictedPackageCache::getBaseDir()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
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 mixed
36
	 */
37
	public function getBaseDir() {
38
		return $this->baseDir;
39
	}
40
41
	/**
42
	 * Return the filename of the generated package, retrieving from cache or generating as necessary
43
	 *
44
	 * @param PackageGenerator $generator The generator to use to create cache entries.
45
	 * @param string $identifier A unique identifier for the generator; used to partition the cache
46
	 * @param string $sha The SHA of the commit to be deployed
47
	 * @param string $repositoryDir The directory where the repository resides
48
	 * @param DeploynautLogFile $log The log to write status output to, including package-generation commands
49
	 *
50
	 * @return string
51
	 */
52
	public function getPackageFilename(
53
		PackageGenerator $generator,
54
		$identifier, $sha,
55
		$repositoryDir,
56
		DeploynautLogFile $log
57
	) {
58
		if(!$this->baseDir) {
59
			throw new \LogicException("Can't use PackageCache without setting BaseDir");
60
		}
61
62
		$buildPath = $this->baseDir . '/' . $this->sanitiseDirName($identifier);
63
		$filename = "$buildPath/$sha.tar.gz";
64
65
		if(!file_exists($this->baseDir)) {
66
			if(!mkdir($this->baseDir)) {
67
				throw new \LogicException("Can't create base dir {$this->baseDir}");
68
			}
69
		}
70
		if(!file_exists($buildPath)) {
71
			if(!mkdir($buildPath)) {
72
				throw new \LogicException("Can't create build path $buildPath");
73
			}
74
		}
75
76
		if(file_exists($filename)) {
77
			$log->write("Using previously generated package $filename");
78
			// This will ensure that our cache garbage collection will remove least-recently-accessed,
79
			// rather than oldest.
80
			touch($filename);
81
			return $filename;
82
83
		} else {
84
			if($this->cacheSize) {
85
				$this->reduceDirSizeTo($buildPath, $this->cacheSize - 1, $log);
86
			}
87
88
			if($generator->generatePackage($sha, $repositoryDir, $filename, $log)) {
89
				return $filename;
90
			}
91
		}
92
	}
93
94
	/**
95
	 * Take the identifier an make it safe to use as a directory name.
96
	 *
97
	 * @param string $identifier The unsanitised directory name.
98
	 */
99
	protected function sanitiseDirName($identifier) {
100
		$safe = preg_replace('/[^A-Za-z0-9_-]/', '', $identifier);
101
		return $safe ? $safe : 'null';
102
	}
103
104
	/**
105
	 * Delete items in this directory until the number of items is <= $count.
106
	 * Delete the oldest files first.
107
	 *
108
	 * @param string $dir The directory to remove items from
109
	 * @param int $count The maximum number of .tar.gz files that can appear in that directory
110
	 * @param DeploynautLogFile $log The log to send removal status messages to
111
	 */
112
	protected function reduceDirSizeTo($dir, $count, DeploynautLogFile $log) {
113
		$files = glob($dir . '/*.tar.gz');
114
		if(sizeof($files) > $count) {
115
			usort($files, function($a, $b) {
116
				return filemtime($a) > filemtime($b);
117
			});
118
119
			for($i = 0; $i < sizeof($files) - $count; $i++) {
120
				$log->write("Removing " . $files[$i] . " from package cache");
121
				unlink($files[$i]);
122
			}
123
		}
124
	}
125
}
126