nemiah / phpFinTS

PHP library to communicate with FinTS/HBCI servers
MIT License
130 stars 42 forks source link

TYPE ERROR in GetDepotAufstellung -> MT535 -> parseHoldings -> Bereitstellungsdatum #451

Open SegFaulty opened 2 months ago

SegFaulty commented 2 months ago

breaks GetDepotAufstellung

was already marked as weak in MT535.php:

 $holding->setDate($this->getDate($r[1]));
  // TODO The time code looks wrong.
  if ($iwn[1] == 'C') {
      preg_match('/^.{14}(.{6})/sm', $iwn[2], $r);
      $holding->setTime($r[1]);
  } else {

$holding->setTime($r[1]); expecting a DateTime Object, gets string

my fix was a complete rewrite of this block:

OLD:

          // Bereitstellungsdatum
            // :98A::PRIC//20210304
            if (preg_match('/:98([AC])::(.*?):/sm', $block, $iwn)) {
                preg_match('/^.{6}(.{8})/sm', $iwn[2], $r);
                $holding->setDate($this->getDate($r[1]));
                // TODO The time code looks wrong.
                if ($iwn[1] == 'C') {
                    preg_match('/^.{14}(.{6})/sm', $iwn[2], $r);
                    $holding->setTime($r[1]);
                } else {
                    $time = new \DateTime();
                    $time->setTime(0, 0);
                    $holding->setTime($time);
                }
            }

            $result->addHolding($holding);
        }
        return $result;
    }

    protected function getDate(string $val): \DateTime
    {
        preg_match('/(\d{4})(\d{2})(\d{2})/', $val, $m);
        try {
            return new \DateTime($m[1] . '-' . $m[2] . '-' . $m[3]);
        } catch (\Exception $e) {
            throw new \InvalidArgumentException("Invalid date: $val", 0, $e);
        }
    }
}

replaced with NEW:

            // Bereitstellungsdatum
            // :98A::PRIC//20210304
            if (preg_match('~:98[AC]::PRIC//(\d{8})(\d{6})?~sm', $block, $r)){
                $date = new \DateTime(substr($r[1],0,4).'-'.substr($r[1],4,2).'-'.substr($r[1],6,2));
                $time = $date;
                if( !empty($r[2]) ){
                    $time->setTime(substr($r[2],0,2), substr($r[1],2,2), substr($r[1],4,2));
                }
                $holding->setDate($date);
                $holding->setTime($time);
            }else{
                // not found
                $holding->setDate(new \DateTime('today'));
                $holding->setTime(new \DateTime('now'));
            }

            $result->addHolding($holding);
        }
        return $result;
    }
} // end of class

Sidenote: I do not understad why there is a separate date and time value in holding
... but in the intend of the old method, time as a DateTime gets the current date by default .. seems not right ... so I set "date" with time 0:0:0 and "time" with the correct date ... so I can later use $holding->getTime()->getTimstamp() to get a plausible timestamp