Mailhook::openDumpFileTail()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
cc 2
nc 2
nop 0
1
<?php
2
/**
3
 * Defines Ingenerator\Mailhook\Mailhook
4
 *
5
 * @author    Andrew Coulton <[email protected]>
6
 * @copyright 2014 inGenerator Ltd
7
 * @licence   BSD
8
 */
9
10
namespace Ingenerator\Mailhook;
11
12
use Ingenerator\Mailhook\Assert\NegativeAssertionRunner;
13
use Ingenerator\Mailhook\Assert\PositiveAssertionRunner;
14
15
/**
16
 * Manages the postfix mail dump, retrieving and parsing new emails
17
 *
18
 * @package Ingenerator\Mailhook
19
 * @see     spec\Ingenerator\Mailhook\MailhookSpec
20
 */
21
class Mailhook {
22
23
	/**
24
	 * @var MailhookAsserter
25
	 */
26
	protected $asserter;
27
28
	/**
29
	 * @var string
30
	 */
31
	protected $dump_file;
32
33
	/**
34
	 * @var int
35
	 */
36
	protected $dump_tail_psn;
37
38
	/**
39
	 * @var Email[]
40
	 */
41
	protected $emails = array();
42
43
	/**
44
	 * @var EmailParser
45
	 */
46
	protected $parser;
47
48
	/**
49
	 * @param string           $dump_file
50
	 * @param null|EmailParser $parser
51
	 */
52
	public function __construct($dump_file, EmailParser $parser = NULL)
53
	{
54
		$this->dump_file = $dump_file;
55
		$this->parser    = $parser ? $parser : new EmailParser;
56
	}
57
58
	public function assert()
59
	{
60
		if ( ! $this->asserter)
61
		{
62
			$filterer       = new EmailListFilterer;
63
			$this->asserter = new MailhookAsserter(
64
				new PositiveAssertionRunner($this, $filterer),
65
				new NegativeAssertionRunner($this, $filterer)
66
			);
67
		}
68
69
		return $this->asserter;
70
	}
71
72
	/**
73
	 * @return Email[]
74
	 */
75
	public function getEmails()
76
	{
77
		return $this->emails;
78
	}
79
80
	/**
81
	 * @return bool
82
	 */
83
	public function hasEmails()
84
	{
85
		return (bool) \count($this->emails);
86
	}
87
88
	/**
89
	 * Remove the current dump file and purge all stored emails
90
	 *
91
	 * @throws \RuntimeException
92
	 */
93
	public function purge()
94
	{
95
		if (\file_exists($this->dump_file))
96
		{
97
			$deleted = (\is_writable($this->dump_file) AND \unlink($this->dump_file));
98
			if ( ! $deleted)
99
			{
100
				throw new \RuntimeException('Could not delete mail dump at ' . $this->dump_file);
101
			}
102
		}
103
		$this->emails        = array();
104
		$this->dump_tail_psn = NULL;
105
	}
106
107
	/**
108
	 * Load new mails from the dump
109
	 *
110
	 * @return void
111
	 */
112
	public function refresh()
113
	{
114
		$mails = $this->loadNewMailsFromDump();
115
		foreach ($mails as $mail)
116
		{
117
			$this->emails[] = $this->parser->parse($mail);
118
		}
119
	}
120
121
	/**
122
	 * @return string[]
123
	 */
124
	protected function loadNewMailsFromDump()
125
	{
126
		$mails = array();
127
128
		if ( ! \file_exists($this->dump_file))
129
		{
130
			return $mails;
131
		}
132
133
		$dump = $this->openDumpFileTail();
134
135
		$mail_index = 0;
136
		while ($line = \fgets($dump))
137
		{
138
			// Identify the start of a new message
139
			if ($this->isMailDumpSeparatorLine($line))
140
			{
141
				$mail_index++;
142
				$mails[$mail_index] = '';
143
			}
144
			$mails[$mail_index] .= $line;
145
		}
146
147
		$this->closeDumpFileTail($dump);
148
149
		return $mails;
150
	}
151
152
	/**
153
	 * @return resource
154
	 */
155
	protected function openDumpFileTail()
156
	{
157
		$dump = \fopen($this->dump_file, 'r');
158
		if ($this->dump_tail_psn)
159
		{
160
			\fseek($dump, $this->dump_tail_psn);
161
		}
162
163
		return $dump;
164
	}
165
166
	/**
167
	 * @param string $line
168
	 *
169
	 * @return int
170
	 */
171
	protected function isMailDumpSeparatorLine($line)
172
	{
173
		return \preg_match('/^From [^\s]+\s*[\w ]+?[0-9]{2}:[0-9]{2}:[0-9]{2} [0-9]{4}$/m', $line);
174
	}
175
176
	/**
177
	 * @param resource $dump
178
	 */
179
	protected function closeDumpFileTail($dump)
180
	{
181
		$this->dump_tail_psn = \ftell($dump);
182
		\fclose($dump);
183
	}
184
}
185