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 | |||
3 | namespace Wabel\CertainAPI\Services; |
||
4 | |||
5 | use Logger\Formatters\DateTimeFormatter; |
||
6 | use Mouf\Utils\Common\Lock; |
||
7 | use Mouf\Utils\Log\Psr\MultiLogger; |
||
8 | use Symfony\Component\Console\Logger\ConsoleLogger; |
||
9 | use Symfony\Component\Console\Output\OutputInterface; |
||
10 | use Wabel\CertainAPI\Helpers\FileChangesHelper; |
||
11 | use Wabel\CertainAPI\Interfaces\CertainListener; |
||
12 | use Wabel\CertainAPI\Ressources\AppointmentsCertain; |
||
13 | |||
14 | class DetectAppointmentsChangingsService |
||
15 | { |
||
16 | |||
17 | /** |
||
18 | * @var AppointmentsCertain |
||
19 | */ |
||
20 | private $appointmentsCertain; |
||
21 | /** |
||
22 | * @var MultiLogger |
||
23 | */ |
||
24 | private $logger; |
||
25 | /** |
||
26 | * @var Lock |
||
27 | */ |
||
28 | private $lock; |
||
29 | /** |
||
30 | * @var string |
||
31 | */ |
||
32 | private $dirPathHistoryAppointments; |
||
33 | /** |
||
34 | * @var CertainListener[] |
||
35 | */ |
||
36 | private $listeners; |
||
37 | |||
38 | /** |
||
39 | * @param CertainListener[] $listeners |
||
40 | */ |
||
41 | public function __construct(AppointmentsCertain $appointmentsCertain, MultiLogger $logger, Lock $lock, string $dirPathHistoryAppointments, array $listeners = []) |
||
42 | { |
||
43 | $this->appointmentsCertain = $appointmentsCertain; |
||
44 | $this->logger = $logger; |
||
45 | $this->lock = $lock; |
||
46 | $this->dirPathHistoryAppointments = $dirPathHistoryAppointments; |
||
47 | $this->listeners = $listeners; |
||
48 | } |
||
49 | |||
50 | /** |
||
51 | * @param $eventCode |
||
52 | * @param null|int $start |
||
53 | * @param null|int $maxResult |
||
54 | * @return mixed |
||
55 | */ |
||
56 | public function getCurrentAppoiments($eventCode, $start = null, $maxResult = null) |
||
57 | { |
||
58 | if (!$start) { |
||
0 ignored issues
–
show
|
|||
59 | $start = 0; |
||
60 | } |
||
61 | if (!$maxResult) { |
||
0 ignored issues
–
show
The expression
$maxResult of type null|integer is loosely compared to false ; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
![]() |
|||
62 | $maxResult = 999999; |
||
63 | } |
||
64 | return $this->certainAppointmentsList = $this->appointmentsCertain->get($eventCode, ['start_index' => $start, 'max_results' => $maxResult])->getResults()->appointments; |
||
0 ignored issues
–
show
The property
certainAppointmentsList 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;
![]() |
|||
65 | } |
||
66 | |||
67 | /** |
||
68 | * @param array $appointmentsOld |
||
69 | * @param array $appointmentsNew |
||
70 | * @return bool |
||
71 | */ |
||
72 | public function hasChanged(array $appointmentsOld, array $appointmentsNew) |
||
73 | { |
||
74 | $hasChanged = false; |
||
75 | $appointmentsOld = self::recursiveArrayObjectToFullArray($appointmentsOld); |
||
76 | $appointmentsNew = self::recursiveArrayObjectToFullArray($appointmentsNew); |
||
77 | //Has change by update or delete |
||
78 | foreach ($appointmentsOld as $appointmentOld) { |
||
79 | if (!in_array($appointmentOld, $appointmentsNew)) { |
||
80 | $hasChanged = true; |
||
81 | break; |
||
82 | } |
||
83 | } |
||
84 | //Has changes by insertion or update |
||
85 | if (!$hasChanged) { |
||
86 | foreach ($appointmentsNew as $appointmentNew) { |
||
87 | if (!in_array($appointmentNew, $appointmentsOld)) { |
||
88 | $hasChanged = true; |
||
89 | break; |
||
90 | } |
||
91 | } |
||
92 | } |
||
93 | return $hasChanged; |
||
94 | } |
||
95 | |||
96 | /** |
||
97 | * @param $object |
||
98 | * @return array |
||
99 | */ |
||
100 | public static function objectToArray($object) |
||
101 | { |
||
102 | if (is_object($object)) { |
||
103 | |||
104 | return (array)$object; |
||
105 | } |
||
106 | return $object; |
||
107 | } |
||
108 | |||
109 | /** |
||
110 | * @param $appointments |
||
111 | * @return array |
||
112 | */ |
||
113 | public static function recursiveArrayObjectToFullArray($appointments) |
||
114 | { |
||
115 | return json_decode(json_encode($appointments), true); |
||
116 | } |
||
117 | |||
118 | /** |
||
119 | * @param array $arrayOlds |
||
120 | * @param array $arrayNews |
||
121 | * @return array |
||
122 | */ |
||
123 | private function arrayRecursiveDiff(array $arrayOlds, array $arrayNews) |
||
124 | { |
||
125 | $difference = []; |
||
126 | foreach ($arrayOlds as $key => $arrayOld) { |
||
127 | if (!in_array($arrayOld, $arrayNews)) { |
||
128 | $difference[$key] = $arrayOld; |
||
129 | } |
||
130 | } |
||
131 | return $difference; |
||
132 | } |
||
133 | |||
134 | /** |
||
135 | * @param array $arrayOlds |
||
136 | * @param array $arrayNews |
||
137 | * @return array |
||
138 | */ |
||
139 | private function arrayRecursiveDiffNew(array $arrayOlds, array $arrayNews) |
||
140 | { |
||
141 | $difference = []; |
||
142 | $appointmentIdOlds = array_map(function($appointment) { |
||
143 | return $appointment['appointmentId']; |
||
144 | }, $arrayOlds); |
||
145 | foreach ($arrayNews as $key => $arrayNew) { |
||
146 | if (!in_array($arrayNew['appointmentId'], $appointmentIdOlds)) { |
||
147 | $difference[$key] = $arrayNew; |
||
148 | } |
||
149 | } |
||
150 | return $difference; |
||
151 | } |
||
152 | |||
153 | /** |
||
154 | * @param array $appointmentsOld |
||
155 | * @param array $appointmentsNew |
||
156 | * @return array |
||
157 | */ |
||
158 | public function getListChangings(array $appointmentsOld, array $appointmentsNew) |
||
159 | { |
||
160 | $appointmentsOld = self::recursiveArrayObjectToFullArray($appointmentsOld); |
||
161 | $appointmentsNew = self::recursiveArrayObjectToFullArray($appointmentsNew); |
||
162 | $changesListUpdateOrDelete = []; |
||
163 | $changesListInsert = []; |
||
164 | if ($this->hasChanged($appointmentsOld, $appointmentsNew)) { |
||
165 | $changesListUpdateOrDelete = self::recursiveArrayObjectToFullArray($this->arrayRecursiveDiff($appointmentsOld, $appointmentsNew)); |
||
166 | $changesListInsert = self::recursiveArrayObjectToFullArray($this->arrayRecursiveDiffNew($appointmentsOld, $appointmentsNew)); |
||
167 | } |
||
168 | return [ |
||
169 | 'inserted' => $changesListInsert, |
||
170 | 'updated_deleted' => $changesListUpdateOrDelete |
||
171 | ]; |
||
172 | } |
||
173 | |||
174 | /** |
||
175 | * |
||
176 | * @param array $currentAppointments |
||
177 | * @param array $changingsDetected |
||
178 | * @return array ['deleted'=>[],'updated'=>[]] |
||
179 | */ |
||
180 | public function detectDeleteOrUpdated(array $currentAppointments, array $changingsDetected) |
||
181 | { |
||
182 | $delete = []; |
||
183 | $update = []; |
||
184 | //@Todo: Detect Fields has changed |
||
185 | $appointmentsNew = self::recursiveArrayObjectToFullArray($currentAppointments); |
||
186 | $changings = self::recursiveArrayObjectToFullArray($changingsDetected); |
||
187 | foreach ($changings as $changing) { |
||
188 | $registration = $changing['registration']['regCode']; |
||
189 | $registrationTarget = $changing['targetRegistration']['regCode']; |
||
190 | $runInNew = 0; |
||
191 | foreach ($appointmentsNew as $currentAppointment) { |
||
192 | $registrationCurrent = $currentAppointment['registration']['regCode']; |
||
193 | $registrationTargetCurrent = $currentAppointment['targetRegistration']['regCode']; |
||
194 | if (in_array($registration, [$registrationCurrent, $registrationTargetCurrent]) |
||
195 | && in_array($registrationTarget, [$registrationCurrent, $registrationTargetCurrent]) |
||
196 | && !in_array($changing, $update) && !in_array($changing, $delete)) { |
||
197 | $update[] = $currentAppointment; |
||
198 | break; |
||
199 | } |
||
200 | $runInNew++; |
||
201 | } |
||
202 | if ($runInNew === count($appointmentsNew) && !in_array($changing, $delete)) { |
||
203 | $delete[] = $changing; |
||
204 | } |
||
205 | |||
206 | } |
||
207 | return [ |
||
208 | 'deleted' => $delete, |
||
209 | 'updated' => $update, |
||
210 | ]; |
||
211 | } |
||
212 | |||
213 | /** |
||
214 | * @param array $appointments |
||
215 | * @param $timestamp |
||
216 | * @return array |
||
217 | */ |
||
218 | public static function insertDateTimeChanges(array $appointments, $timestamp) |
||
219 | { |
||
220 | foreach ($appointments as $key => $appointment) { |
||
221 | $appointments[$key]['dateDetectChanges'] = $timestamp; |
||
222 | } |
||
223 | return $appointments; |
||
224 | } |
||
225 | |||
226 | /** |
||
227 | * @param array $appointmentsOld |
||
228 | * @param array $appointmentsNew |
||
229 | * @param string $timestamp |
||
230 | * @return array ['deleted'=>[],'updated'=>[]] |
||
231 | */ |
||
232 | public function detectAppointmentsChangings(array $appointmentsOld, array $appointmentsNew, $timestamp) |
||
233 | { |
||
234 | $changings = $this->getListChangings($appointmentsOld, $appointmentsNew); |
||
235 | $changesList = $this->detectDeleteOrUpdated($appointmentsNew, $changings['updated_deleted']); |
||
236 | $changesList['inserted'] = self::insertDateTimeChanges($changings['inserted'], $timestamp); |
||
237 | $changesList['updated'] = self::insertDateTimeChanges($changesList['updated'], $timestamp); |
||
238 | $changesList['deleted'] = self::insertDateTimeChanges($changesList['deleted'], $timestamp); |
||
239 | return $changesList; |
||
240 | } |
||
241 | |||
242 | public function runCommandForEvent(string $eventCode, OutputInterface $output = null): void |
||
243 | { |
||
244 | if ($output) { |
||
245 | $this->logger->addLogger(new DateTimeFormatter(new ConsoleLogger($output))); |
||
246 | } |
||
247 | $codeCheckDirectory = FileChangesHelper::checkDirectory($this->dirPathHistoryAppointments); |
||
248 | if ($codeCheckDirectory === 'no_directory') { |
||
249 | $this->logger->error('Path ' . $this->dirPathHistoryAppointments . ' doesn\'t exists.'); |
||
250 | return; |
||
251 | } |
||
252 | if ($codeCheckDirectory === 'not_readable') { |
||
253 | $this->logger->error('Path ' . $this->dirPathHistoryAppointments . ' is not readable.'); |
||
254 | return; |
||
255 | } |
||
256 | if ($codeCheckDirectory === 'not_writable') { |
||
257 | $this->logger->error('Path ' . $this->dirPathHistoryAppointments . ' is not writable.'); |
||
258 | return; |
||
259 | } |
||
260 | $this->lock->acquireLock(); |
||
261 | $this->logger->info('Detect changes - Run.'); |
||
262 | //That permits to stop the followings instructions when we are makings changes on Certain. |
||
263 | //Get the online appointments. |
||
264 | $appointmentsNewCertain = $this->getCurrentAppoiments($eventCode); |
||
265 | $appointmentsNew = self::recursiveArrayObjectToFullArray($appointmentsNewCertain); |
||
266 | //Get the last saved appointments to get old data. |
||
267 | $appointmentsOldHistoryFilePath = FileChangesHelper::getTheLastAppointmentsSaved($eventCode, $this->dirPathHistoryAppointments); |
||
268 | if (!$appointmentsOldHistoryFilePath) { |
||
269 | //No files so it's the first time we attempt to synchronize. |
||
270 | $appointmentsOld = []; |
||
271 | } else { |
||
272 | //Get the last old appointments data. |
||
273 | $appointmentsOldHistory = FileChangesHelper::getJsonContentFromFile($appointmentsOldHistoryFilePath); |
||
274 | $appointmentsOld = self::recursiveArrayObjectToFullArray($appointmentsOldHistory); |
||
275 | } |
||
276 | //Check if they are changes. |
||
277 | $timestamp = time(); |
||
278 | // $listChangings = $this->detectAppointmentsChangings($appointmentsOld, $appointmentsNew, $timestamp); |
||
279 | $listChangings = $this->detectAppointmentsChanges($appointmentsOld, $appointmentsNew, $timestamp); |
||
280 | if (!$appointmentsOld || ((isset($listChangings['updated']) && !empty($listChangings['updated'])) |
||
0 ignored issues
–
show
The expression
$appointmentsOld 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 ![]() |
|||
281 | || (isset($listChangings['deleted']) && !empty($listChangings['deleted'])) || (isset($listChangings['inserted']) && !empty($listChangings['inserted'])))) { |
||
282 | //Changes? So we save the new online appointments |
||
283 | FileChangesHelper::saveAppointmentsFileByHistory($this->dirPathHistoryAppointments . '/appointments_' . $eventCode . '.json', json_encode($appointmentsNew)); |
||
284 | $this->logger->info('Detect changes - Save Changes'); |
||
285 | } else { |
||
286 | $this->logger->info('Detect changes - No Changes'); |
||
287 | } |
||
288 | FileChangesHelper::saveAppointmentsFileByHistory($this->dirPathHistoryAppointments . '/changes_' . $eventCode . '.json', json_encode($listChangings)); |
||
289 | foreach ($this->listeners as $listener) { |
||
290 | //Run Listener. For instance,Here we can use ChangingsToFileListeners to save the changes in file. |
||
291 | $listener->run($eventCode, $listChangings); |
||
292 | } |
||
293 | $this->logger->info('Detect changes - Stop.'); |
||
294 | $this->lock->releaseLock(); |
||
295 | } |
||
296 | |||
297 | private function detectAppointmentsChanges(array $appointmentsOld, array $appointmentsNew, int $timestamp) |
||
298 | { |
||
299 | $oldAppointments = []; |
||
300 | $newAppointments = []; |
||
301 | $oldAppointmentIds = []; |
||
302 | $newAppointmentIds = []; |
||
303 | |||
304 | foreach ($appointmentsOld as $item) { |
||
305 | $oldAppointments[$item['appointmentId']] = $item; |
||
306 | $oldAppointmentIds[] = $item['appointmentId']; |
||
307 | } |
||
308 | foreach ($appointmentsNew as $item) { |
||
309 | $newAppointments[$item['appointmentId']] = $item; |
||
310 | $newAppointmentIds[] = $item['appointmentId']; |
||
311 | } |
||
312 | |||
313 | $data = [ |
||
314 | 'inserted' => [], |
||
315 | 'updated' => [], |
||
316 | 'deleted' => [], |
||
317 | ]; |
||
318 | |||
319 | $data['inserted'] = array_map(static function($item) use ($newAppointments) { |
||
320 | return $newAppointments[$item]; |
||
321 | }, array_diff($newAppointmentIds, $oldAppointmentIds)); |
||
322 | |||
323 | $data['deleted'] = array_map(static function($item) use ($oldAppointments) { |
||
324 | return $oldAppointments[$item]; |
||
325 | }, array_diff($oldAppointmentIds, $newAppointmentIds)); |
||
326 | |||
327 | $updatedIds = array_keys(array_intersect_key($newAppointments, $oldAppointments)); |
||
328 | foreach ($updatedIds as $updatedId) { |
||
329 | // Important we use simple != and not strict !== to avoid issue if positions in array are different |
||
330 | if ($newAppointments[$updatedId] != $oldAppointments[$updatedId]) { |
||
331 | $data['updated'][] = $newAppointments[$updatedId]; |
||
332 | } |
||
333 | } |
||
334 | |||
335 | $data['inserted'] = self::insertDateTimeChanges($data['inserted'], $timestamp); |
||
336 | $data['updated'] = self::insertDateTimeChanges($data['updated'], $timestamp); |
||
337 | $data['deleted'] = self::insertDateTimeChanges($data['deleted'], $timestamp); |
||
338 | |||
339 | return $data; |
||
340 | } |
||
341 | |||
342 | } |
||
343 |
In PHP, under loose comparison (like
==
, or!=
, orswitch
conditions), values of different types might be equal.For
integer
values, zero is a special case, in particular the following results might be unexpected: