Task::run()
last analyzed

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 1
nc 1
1
<?php
2
/*
3
 * This file is part of Yolk - Gamer Network's PHP Framework.
4
 *
5
 * Copyright (c) 2013 Gamer Network Ltd.
6
 * 
7
 * Distributed under the MIT License, a copy of which is available in the
8
 * LICENSE file that was bundled with this package, or online at:
9
 * https://github.com/gamernetwork/yolk-core
10
 */
11
12
namespace yolk\console;
13
14
abstract class Task {
15
16
	const LOCK_DIR = '/tmp';
17
	const PS_EXEC  = 'ps --no-heading -p ';
18
19
	abstract public function run( array $args );
20
21
	public function __invoke() {
22
23
		if( !$this->lock() )
24
			throw new \RuntimeException(sprintf('An instance of \\%s is already running', get_class($this)));
25
26
		$result = $this->run(
27
			$this->parseArgs()
28
		);
29
30
		$this->unlock();
31
32
		return $result;
33
34
	}
35
36
	/**
37
	 * parseArgs Command Line Interface (CLI) utility function.
38
	 * @author   Patrick Fisher <[email protected]>
39
	 * @see      https://github.com/pwfisher/CommandLine.php
40
	 */
41
	protected function parseArgs( $argv = null ) {
42
		$argv = $argv ? $argv : $_SERVER['argv']; array_shift($argv); $o = [];
43
		for ($i = 0, $j = count($argv); $i < $j; $i++) { $a = $argv[$i];
44
			if (substr($a, 0, 2) == '--') { $eq = strpos($a, '=');
45
				if ($eq !== false) { $o[substr($a, 2, $eq - 2)] = substr($a, $eq + 1); }
46 View Code Duplication
				else { $k = substr($a, 2);
47
					if ($i + 1 < $j && $argv[$i + 1][0] !== '-') { $o[$k] = $argv[$i + 1]; $i++; }
48
					else if (!isset($o[$k])) { $o[$k] = true; } } }
49
			else if (substr($a, 0, 1) == '-') {
50
				if (substr($a, 2, 1) == '=') { $o[substr($a, 1, 1)] = substr($a, 3); }
51 View Code Duplication
				else {
52
					foreach (str_split(substr($a, 1)) as $k) { if (!isset($o[$k])) { $o[$k] = true; } }
53
					if ($i + 1 < $j && $argv[$i + 1][0] !== '-') { $o[$k] = $argv[$i + 1]; $i++; } } }
54
			else { $o[] = $a; } }
55
		return $o;
56
	}
57
58
	/**
59
	 * Return the name of the lock file used by this task.
60
	 * @return string
61
	 */
62
	protected function getLockFile() {
63
		return rtrim(static::LOCK_DIR, '/'). '/'. strtolower(str_replace('\\', '-', get_class($this)));
64
	}
65
66
	protected function isLocked() {
67
68
		// check if lock file exists
69
		$locked = file_exists($lock = $this->getLockFile());
70
71
		// if it does then check the process is actually still running
72
		if( $locked ) {
73
			$pid = file_get_contents($lock);
74
			$locked = exec(static::PS_EXEC. $pid) != '';
75
			// no such process so remove the lock file
76
			if( !$locked )
77
				$this->unlock();
78
		}
79
80
		return $locked;
81
82
	}
83
84
	protected function lock() {
85
86
		// locked by running process
87
		if( $this->isLocked() )
88
			return false;
89
90
		// open the lock file only if it doesn't exist
91
		if( !$fh = @fopen($this->getLockFile(), 'x') )
92
			return false;
93
94
		// clear it and write out the new PID
95
		ftruncate($fh, 0);
96
		fwrite($fh, getmypid());
97
		fclose($fh);
98
99
		return true;
100
101
	}
102
103
	protected function unlock() {
104
105
		$lock = $this->getLockFile();
106
107
		if( file_exists($lock) ) {
108
109
			// PIDs don't match - ooops!
110
			if( file_get_contents($lock) != getmypid() )
111
				throw new \RuntimeException('Lock file contains incorrect PID');
112
113
			// Delete the lock file
114
			if( !unlink($lock) )
115
				throw new \RuntimeException('Unable to remove lock file');
116
117
		}
118
119
	}
120
121
}
122
123
// EOF