Passed
Push — developer ( 4e3135...f5c82a )
by Radosław
30:25 queued 12:59
created

Base::findRelatedRecordsByEmail()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0
1
<?php
2
/**
3
 * Base mail scanner action file.
4
 *
5
 * @package App
6
 *
7
 * @copyright YetiForce S.A.
8
 * @license   YetiForce Public License 5.0 (licenses/LicenseEN.txt or yetiforce.com)
9
 * @author    Mariusz Krzaczkowski <[email protected]>
10
 * @author    Radosław Skrzypczak <[email protected]>
11
 */
12
13
namespace App\Mail\ScannerAction;
14
15
/**
16
 * Base mail scanner action class.
17
 */
18
abstract class Base
19
{
20
	/** @var int Action priority. */
21
	public static $priority = 9;
22
23
	/** @var string[] Scope of availability. */
24
	public static $available = ['Users', 'MailAccount'];
25
26
	/** @var string Action label */
27
	protected $label;
28
29
	/** @var \App\Mail\Message\Imap Message instance. */
30
	protected $message;
31
32
	/**
33
	 * Get action name.
34
	 * Action name | File name.
35
	 *
36
	 * @return string
37
	 */
38
	public function getName(): string
39
	{
40
		return basename(str_replace('\\', '/', static::class));
41
	}
42
43
	/**
44
	 * Main function to execute action.
45
	 *
46
	 * @return void
47
	 */
48
	abstract public function process(): void;
49
50
	/**
51
	 * Set mail account.
52
	 *
53
	 * @param \App\Mail\Account $account
54
	 *
55
	 * @return $this
56
	 */
57
	public function setAccount(\App\Mail\Account $account)
58
	{
59
		$this->account = $account;
0 ignored issues
show
Bug Best Practice introduced by
The property account does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
60
		return $this;
61
	}
62
63
	public function setMessage(\App\Mail\Message\Base $message)
64
	{
65
		$this->message = $message;
0 ignored issues
show
Documentation Bug introduced by
$message is of type App\Mail\Message\Base, but the property $message was declared to be of type App\Mail\Message\Imap. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
66
		return $this;
67
	}
68
69
	public function findRelatedRecords(bool $onlyId = false): array
70
	{
71
		$ids = $this->findRelatedRecordsByEmail();
72
		if ($idsBySubject = $this->findRelatedRecordsBySubject()) {
73
			$ids[] = current($idsBySubject);
74
		}
75
		if (!$onlyId) {
76
			foreach ($ids as &$id) {
77
				$id = [
78
					'id' => $id,
79
					'module' => \App\Record::getType($id),
80
					'label' => \App\Record::getLabel($id),
81
				];
82
			}
83
		}
84
		return $ids;
85
	}
86
87
	public function findRelatedRecordsByEmail(): array
88
	{
89
		if (!isset($this->message->processData['findByEmail'])) {
90
			$emails = array_unique(array_merge($this->message->getEmail('from'), $this->message->getEmail('to'), $this->message->getEmail('cc'), $this->message->getEmail('bcc')));
91
			$this->message->setProcessData('findByEmail', \App\Utils::flatten(\App\Mail\RecordFinder::findByEmail($emails, $this->getEmailsFields())));
92
		}
93
94
		return $this->message->processData['findByEmail'];
95
	}
96
97
	public function findRelatedRecordsBySubject(): array
98
	{
99
		if (!isset($this->message->processData['findBySubject'])) {
100
			$this->message->processData['findBySubject'] = \App\Mail\RecordFinder::findBySubject($this->message->getSubject(), $this->getNumberFields());
101
		}
102
103
		return $this->message->processData['findBySubject'];
104
	}
105
106
	public function getEmailsFields(?string $searchModuleName = null): array
107
	{
108
		$cacheKey = $searchModuleName ?? '-';
109
		if (isset($this->emailsFieldsCache[$cacheKey])) {
110
			return $this->emailsFieldsCache[$cacheKey];
111
		}
112
113
		$fields = [];
114
		if ($mailScannerFields = $this->account->getSource()->get('scanner_fields')) {
115
			foreach (explode(',', trim($mailScannerFields, ',')) as $field) {
116
				$field = explode('|', $field);
117
				if (($searchModuleName && $searchModuleName !== $field[1]) || !\in_array($field[3], [13, 319])) {
118
					continue;
119
				}
120
				$fields[$field[1]][$field[3]][] = $field[2];
121
			}
122
		}
123
		$this->emailsFieldsCache[$cacheKey] = $fields;
0 ignored issues
show
Bug Best Practice introduced by
The property emailsFieldsCache does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
124
125
		return $fields;
126
	}
127
128
	public function getNumberFields(?string $searchModuleName = null): array
129
	{
130
		$cacheKey = $searchModuleName ?? '-';
131
		if (isset($this->numberFieldsCache[$cacheKey])) {
132
			return $this->numberFieldsCache[$cacheKey];
133
		}
134
135
		$fields = [];
136
		if ($mailScannerFields = $this->account->getSource()->get('scanner_fields')) {
137
			foreach (explode(',', trim($mailScannerFields, ',')) as $field) {
138
				$field = explode('|', $field);
139
				if (($searchModuleName && $searchModuleName !== $field[1]) || 4 !== (int) $field[3]) {
140
					continue;
141
				}
142
				$fields[$field[1]][$field[3]][] = $field[2];
143
			}
144
		}
145
		$this->numberFieldsCache[$cacheKey] = $fields;
0 ignored issues
show
Bug Best Practice introduced by
The property numberFieldsCache does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
146
147
		return $fields;
148
	}
149
150
	public function checkExceptions(): bool
151
	{
152
		$domainExceptions = array_filter(explode(',', $this->account->getSource()->get('domain_exceptions') ?: ''));
153
		$emailExceptions = array_column(\App\Json::decode($this->account->getSource()->get('email_exceptions') ?: '[]'), 'e');
154
		if ($domainGlobalExceptions = \App\Mail::getConfig('scanner', 'domain_exceptions')) {
155
			$domainExceptions = array_merge($domainExceptions, array_filter(explode(',', $domainGlobalExceptions)));
156
		}
157
		if ($emailGlobalExceptions = \App\Mail::getConfig('scanner', 'email_exceptions')) {
158
			$emailExceptions = array_merge(array_column(\App\Json::decode($emailGlobalExceptions), 'e'));
159
		}
160
		$mails = (0 === $this->message->getMailType()) ? $this->message->getEmail('to') : $this->message->getEmail('from');
161
162
		return $mails && ($domainExceptions || $emailExceptions) && (
0 ignored issues
show
Bug Best Practice introduced by
The expression $mails of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
Bug Best Practice introduced by
The expression $emailExceptions of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
Bug Best Practice introduced by
The expression $domainExceptions of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
163
			\array_intersect($mails, $emailExceptions)
164
			|| \array_intersect(array_map(fn ($email) => (substr(strrchr($email, '@'), 1)), $mails), $domainExceptions)
165
		);
166
	}
167
}
168