This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | $defflip = (!cfip()) ? exit(header('HTTP/1.1 401 Unauthorized')) : 1; |
||
3 | |||
4 | class Notification extends Mail { |
||
5 | var $table = 'notifications'; |
||
6 | var $tableSettings = 'notification_settings'; |
||
7 | |||
8 | public function setInactive($id) { |
||
9 | $field = array( |
||
10 | 'name' => 'active', |
||
11 | 'type' => 'i', |
||
12 | 'value' => 0 |
||
13 | ); |
||
14 | return $this->updateSingle($id, $field); |
||
0 ignored issues
–
show
|
|||
15 | } |
||
16 | |||
17 | /** |
||
18 | * We check our notification table for existing data |
||
19 | * so we can avoid duplicate entries |
||
20 | **/ |
||
21 | public function isNotified($aData) { |
||
22 | $this->debug->append("STA " . __METHOD__, 4); |
||
23 | $data = json_encode($aData); |
||
24 | $stmt = $this->mysqli->prepare("SELECT id FROM $this->table WHERE data = ? AND active = 1 LIMIT 1"); |
||
25 | if ($stmt && $stmt->bind_param('s', $data) && $stmt->execute() && $stmt->store_result() && $stmt->num_rows == 1) { |
||
26 | return true; |
||
27 | } |
||
28 | |||
29 | return $this->sqlError('E0041'); |
||
30 | } |
||
31 | |||
32 | /** |
||
33 | * Get all active notifications |
||
34 | **/ |
||
35 | public function getAllActive($strType) { |
||
36 | $this->debug->append("STA " . __METHOD__, 4); |
||
37 | $stmt =$this->mysqli->prepare("SELECT id, data FROM $this->table WHERE active = 1 AND type = ?"); |
||
38 | if ($stmt && $stmt->bind_param('s', $strType) && $stmt->execute() && $result = $stmt->get_result()) |
||
39 | return $result->fetch_all(MYSQLI_ASSOC); |
||
40 | return $this->sqlError('E0042'); |
||
41 | } |
||
42 | |||
43 | /** |
||
44 | * Add a new notification to the table |
||
45 | * @param type string Type of the notification |
||
46 | * @return bool |
||
47 | **/ |
||
48 | public function addNotification($account_id, $type, $data) { |
||
49 | $this->debug->append("STA " . __METHOD__, 4); |
||
50 | // Store notification data as json |
||
51 | $data = json_encode($data); |
||
52 | $stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, type, data, active) VALUES (?, ?,?,1)"); |
||
53 | if ($stmt && $stmt->bind_param('iss', $account_id, $type, $data) && $stmt->execute()) |
||
54 | return true; |
||
55 | return $this->sqlError('E0043'); |
||
56 | } |
||
57 | |||
58 | /** |
||
59 | * Fetch notifications for a user account |
||
60 | * @param id int Account ID |
||
61 | * @return array Notification data |
||
62 | **/ |
||
63 | public function getNotifications($account_id,$limit=50) { |
||
64 | $this->debug->append("STA " . __METHOD__, 4); |
||
65 | $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE account_id = ? ORDER BY time DESC LIMIT ?"); |
||
66 | if ($stmt && $stmt->bind_param('ii', $account_id, $limit) && $stmt->execute() && $result = $stmt->get_result()) |
||
67 | return $result->fetch_all(MYSQLI_ASSOC); |
||
68 | return $this->getError(); |
||
0 ignored issues
–
show
The return type of
return $this->getError(); (string ) is incompatible with the return type documented by Notification::getNotifications of type array .
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design. Let’s take a look at an example: class Author {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
abstract class Post {
public function getAuthor() {
return 'Johannes';
}
}
class BlogPost extends Post {
public function getAuthor() {
return new Author('Johannes');
}
}
class ForumPost extends Post { /* ... */ }
function my_function(Post $post) {
echo strtoupper($post->getAuthor());
}
Our function ![]() |
|||
69 | } |
||
70 | |||
71 | /** |
||
72 | * Fetch notification settings for user account |
||
73 | * @param id int Account ID |
||
74 | * @return array Notification settings |
||
75 | **/ |
||
76 | public function getNotificationSettings($account_id) { |
||
77 | // Some defaults, we cover them here so we can avoid adding default settings on user creation |
||
78 | $aDefaults = array( 'newsletter' => 1 ); |
||
79 | $this->debug->append("STA " . __METHOD__, 4); |
||
80 | $stmt = $this->mysqli->prepare("SELECT * FROM $this->tableSettings WHERE account_id = ?"); |
||
81 | if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute() && $result = $stmt->get_result()) { |
||
82 | if ($result->num_rows > 0) { |
||
83 | $aFound = array(); |
||
84 | while ($row = $result->fetch_assoc()) { |
||
85 | if (array_key_exists($row['type'], $aDefaults)) $aFound[] = $row['type']; |
||
86 | $aData[$row['type']] = $row['active']; |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
$aData was never initialized. Although not strictly required by PHP, it is generally a good practice to add $aData = array(); before regardless.
Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code. Let’s take a look at an example: foreach ($collection as $item) {
$myArray['foo'] = $item->getFoo();
if ($item->hasBar()) {
$myArray['bar'] = $item->getBar();
}
// do something with $myArray
}
As you can see in this example, the array This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop. ![]() |
|||
87 | } |
||
88 | // Check found types against our defaults, set if required |
||
89 | foreach ($aDefaults as $type => $value) { |
||
90 | if (!in_array($type, $aFound)) { |
||
91 | $aData[$type] = $value; |
||
0 ignored issues
–
show
The variable
$aData does not seem to be defined for all execution paths leading up to this point.
If you define a variable conditionally, it can happen that it is not defined for all execution paths. Let’s take a look at an example: function myFunction($a) {
switch ($a) {
case 'foo':
$x = 1;
break;
case 'bar':
$x = 2;
break;
}
// $x is potentially undefined here.
echo $x;
}
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined. Available Fixes
![]() |
|||
92 | } |
||
93 | } |
||
94 | return $aData; |
||
95 | } else { |
||
96 | foreach ($aDefaults as $type => $value) $aData[$type] = $value; |
||
97 | return $aData; |
||
98 | } |
||
99 | } |
||
100 | return $this->sqlError('E0045'); |
||
101 | } |
||
102 | |||
103 | /** |
||
104 | * Get all accounts that wish to receive a specific notification |
||
105 | * @param strType string Notification type |
||
106 | * @return data array User Accounts |
||
107 | **/ |
||
108 | public function getNotificationAccountIdByType($strType) { |
||
109 | $this->debug->append("STA " . __METHOD__, 4); |
||
110 | $stmt = $this->mysqli->prepare("SELECT account_id FROM $this->tableSettings WHERE type IN (?, ?) AND active = 1 GROUP BY account_id"); |
||
111 | $notStrType = substr('push_'.$strType, 0, 15); |
||
112 | if ($stmt && $stmt->bind_param('ss', $strType, $notStrType) && $stmt->execute() && $result = $stmt->get_result()) { |
||
113 | return $result->fetch_all(MYSQLI_ASSOC); |
||
114 | } |
||
115 | return $this->sqlError('E0046'); |
||
0 ignored issues
–
show
The return type of
return $this->sqlError('E0046'); (boolean ) is incompatible with the return type documented by Notification::getNotificationAccountIdByType of type data .
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design. Let’s take a look at an example: class Author {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
abstract class Post {
public function getAuthor() {
return 'Johannes';
}
}
class BlogPost extends Post {
public function getAuthor() {
return new Author('Johannes');
}
}
class ForumPost extends Post { /* ... */ }
function my_function(Post $post) {
echo strtoupper($post->getAuthor());
}
Our function ![]() |
|||
116 | } |
||
117 | |||
118 | /** |
||
119 | * Update accounts notification settings |
||
120 | * @param account_id int Account ID |
||
121 | * @param data array Data array |
||
122 | * @return bool |
||
123 | **/ |
||
124 | public function updateSettings($account_id, $data) { |
||
125 | $this->debug->append("STA " . __METHOD__, 4); |
||
126 | $failed = $ok = 0; |
||
127 | foreach ($data as $type => $active) { |
||
128 | $stmt = $this->mysqli->prepare("INSERT INTO $this->tableSettings (active, type, account_id) VALUES (?,?,?) ON DUPLICATE KEY UPDATE active = ?"); |
||
129 | if ($stmt && $stmt->bind_param('isii', $active, $type, $account_id, $active) && $stmt->execute()) { |
||
130 | $ok++; |
||
131 | } else { |
||
132 | $failed++; |
||
133 | } |
||
134 | } |
||
135 | if ($failed > 0) { |
||
136 | $this->setErrorMessage($this->getErrorMsg('E0047', $failed)); |
||
137 | return $this->sqlError(); |
||
138 | } |
||
139 | $this->log->log("info", "User $account_id updated notification settings"); |
||
140 | return true; |
||
141 | } |
||
142 | |||
143 | /** |
||
144 | * Send a specific notification setup in notification_settings |
||
145 | * @param type string Notification type |
||
146 | * @return bool |
||
147 | **/ |
||
148 | public function sendNotification($account_id, $strType, $aMailData) { |
||
149 | // Check if we notified for this event already |
||
150 | if ( $this->isNotified($aMailData) ) { |
||
151 | $this->setErrorMessage('A notification for this event has been sent already'); |
||
152 | return false; |
||
153 | } |
||
154 | // Check if this user wants strType notifications |
||
155 | $stmt = $this->mysqli->prepare("SELECT type FROM $this->tableSettings WHERE type IN (?, ?) AND active = 1 AND account_id = ?"); |
||
156 | $notStrType = substr('push_'.$strType, 0, 15); |
||
157 | if ($stmt && $stmt->bind_param('ssi', $strType, $notStrType, $account_id) && $stmt->execute() && $result = $stmt->get_result()) { |
||
158 | $types = array_map(function($a){ return reset($a);}, $result->fetch_all(MYSQLI_ASSOC)); |
||
159 | $stmt->close(); |
||
160 | $result = true; |
||
161 | foreach ($types as $type){ |
||
162 | if (strpos($type, 'push_') === 0){ |
||
163 | if (PushNotification::Instance() instanceof PushNotification){ |
||
164 | $result &= PushNotification::Instance()->sendNotification($account_id, $strType, $aMailData); |
||
165 | } |
||
166 | } else { |
||
167 | $result &= $this->sendMail('notifications/' . $strType, $aMailData); |
||
168 | } |
||
169 | } |
||
170 | if ($result){ |
||
171 | $this->addNotification($account_id, $strType, $aMailData); |
||
172 | return true; |
||
173 | } else { |
||
174 | $this->setErrorMessage('SendMail call failed: ' . $this->getError()); |
||
175 | return false; |
||
176 | } |
||
177 | } else { |
||
178 | $this->setErrorMessage('User disabled ' . $strType . ' notifications'); |
||
179 | return true; |
||
180 | } |
||
181 | $this->setErrorMessage('Error sending mail notification'); |
||
0 ignored issues
–
show
$this->setErrorMessage('...ng mail notification'); does not seem to be reachable.
This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed. Unreachable code is most often the result of function fx() {
try {
doSomething();
return true;
}
catch (\Exception $e) {
return false;
}
return false;
}
In the above example, the last ![]() |
|||
182 | return false; |
||
183 | } |
||
184 | |||
185 | /** |
||
186 | * Cleanup old notifications |
||
187 | * @param none |
||
188 | * @return bool true or false |
||
189 | **/ |
||
190 | public function cleanupNotifications($days=7) { |
||
191 | $failed = 0; |
||
192 | $this->deleted = 0; |
||
0 ignored issues
–
show
The property
deleted does not exist. Did you maybe forget to declare it?
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code: class MyClass { }
$x = new MyClass();
$x->foo = true;
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: class MyClass {
public $foo;
}
$x = new MyClass();
$x->foo = true;
![]() |
|||
193 | $stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE time < (NOW() - INTERVAL ? DAY)"); |
||
194 | View Code Duplication | if (! ($this->checkStmt($stmt) && $stmt->bind_param('i', $days) && $stmt->execute())) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
195 | $failed++; |
||
196 | } else { |
||
197 | $this->deleted += $stmt->affected_rows; |
||
198 | } |
||
199 | View Code Duplication | if ($failed > 0) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
200 | $this->setCronMessage('Failed to delete ' . $failed . ' notifications from ' . $this->table . ' table'); |
||
201 | return false; |
||
202 | } |
||
203 | return true; |
||
204 | } |
||
205 | } |
||
206 | |||
207 | $notification = new Notification(); |
||
208 | $notification->setDebug($debug); |
||
209 | $notification->setLog($log); |
||
210 | $notification->setMysql($mysqli); |
||
211 | $notification->setSmarty($smarty); |
||
212 | $notification->setConfig($config); |
||
213 | $notification->setSetting($setting); |
||
214 | $notification->setErrorCodes($aErrorCodes); |
||
215 |
It seems like the type of the argument is not accepted by the function/method which you are calling.
In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.
We suggest to add an explicit type cast like in the following example: