Forum: PC-Programmierung [PHP] Objekt verschwindet nach Zugriff (Datumsberechnung)


von Marek N. (Gast)


Lesenswert?

Hi,

ich versuche mich gerade an einem kleinen Script, das mir iterativ für 
jedes Jahr eine Seite als Kalender mit allen Feiertagen generiert. Die 
festen Feiertage sind ja trivial, die kann man als Tabelle/Array ablegen 
aber die beweglichen Feiertage muss man jedes Jahr neu berechnen, da sie 
vom Osternsonntag abgeleitet werden. Und genau bei dieser 
Differenzberechnung bin ich auf ein Phänomen getoßen, das ich nicht 
verstehe.

Die Berechnung des eigentlichen Ostersonntags nehme ich mit der Funktion 
*easter_days($current_year)* vor [1]. Anschließende manuelle Korrektur 
(um hier schon Fehler durch die Verrechnung zu vermeiden) auf den 
korrekten Tag und Monat. Abgleich mit diversen Tabellen, z.B. [2], [2a] 
bestätigt die Korrektheit auch in Schaltjahren sowie jenseits des 
Unix-Timestamp-Intervalls.
1
<?php
2
$current_year = 2021;
3
4
// EASTER SUNDAY
5
    // https://www.php.net/manual/de/function.easter-days.php
6
    $easter_days = easter_days($current_year);      // days since MARCH 21th
7
        $easter_sunday_month   = (int)(($easter_days + 21) / 31) + 3;
8
        $easter_sunday_day     = ($easter_days + 21) % 31;
9
            if($easter_sunday_day == 0)
10
            {
11
                // Apr. 0th ~~~> Mar. 31th
12
                $easter_sunday_day = 31;
13
                $easter_sunday_month = 3;
14
            }
15
    echo "\nOstersonntag:\t" .  $current_year . "-" . $easter_sunday_month . "-" . $easter_sunday_day . "\n";
So weit, so happy.

Aus dem Datum des Ostersonntags versuche ich nun, die anderen Feiertage, 
die die eine bestimmte Zeit davor oder danach liegen zu berechnen 
mittels der Funktionen *date_sub()* bzw. *date_add()* [3], [4], die 
eigentlich einen Alias sind für die objektorientierten Befehle 
*DateTime::sub ()* bzw. *DateTime::add ()* [5], [6].

Da ich leider mit Objektorientierung keine Erfahrung habe, halte ich 
mich weiter an den prozeduralen Ansatz. Ich habe verstanden, dass die 
*date_sub()* ein DateTime-Objekt erwartet, das sie bearbeiten kann, 
welches ich wie im Beispiel mit der *date_create()*-Funktion [7] aus 
meinen Ostersonntags-Daten erstelle. Fein!

Erstelle ich jedes Mal dieses Objekt(?) neu, bevor ich mit der 
date_sub() bzw. _add drauf zugreife, funktioniert es wie erwartet:
1
<?php
2
...
3
// ASCHERMITTWOCH:  46 days before Easter Sunday
4
    $date_temp_obj = date_create($current_year . '-' . $easter_sunday_month . '-' . $easter_sunday_day);
5
    date_sub($date_temp_obj, date_interval_create_from_date_string('46 days'));
6
        $temp_day    = date_format($date_temp_obj, 'd');
7
        $temp_month  = date_format($date_temp_obj, 'm');
8
    echo "\nAschermittwoch:\t" .  $current_year . "-" . $temp_month . "-" . $temp_day;
9
10
// KARFREITAG:      2 days before before Easter Sunday
11
    $date_temp_obj = date_create($current_year . '-' . $easter_sunday_month . '-' . $easter_sunday_day);
12
    date_sub($date_temp_obj, date_interval_create_from_date_string('2 days'));
13
        $temp_day    = date_format($date_temp_obj, 'd');
14
        $temp_month  = date_format($date_temp_obj, 'm');
15
    echo "\nKarfreitag:\t" .  $current_year . "-" . $temp_month . "-" . $temp_day;
16
...

Hier kann man sich von der Funktion des vollständigen Quellcodes in 
einer Sandbox überzeugen: 
https://sandbox.onlinephpfunctions.com/code/12373b779ddc9e565e42149aeb83909a7efab4bb

Resultat:
1
THIS WORKS CORRECTLY :-)
2
3
Ostersonntag:  2021-4-4
4
5
Aschermittwoch:  2021-02-17
6
Karfreitag:  2021-04-02
7
Himmelfahrt:  2021-05-13
8
Pfingsten:  2021-05-23

Erstelle ich dieses Objekt jedoch nur ein Mal und speichere ich es in 
einer Variable /$date_easter/ zwischen, um es wiederzuverwenden, erhalte 
ich anfangs das korrekte Ergebis, anschließend aber falsche:
1
<?php
2
...
3
    $date_easter = date_create($current_year . '-' . $easter_sunday_month . '-' . $easter_sunday_day);
4
5
// ASCHERMITTWOCH:  46 days before Easter Sunday
6
    $date_temp_obj = $date_easter;
7
    date_sub($date_temp_obj, date_interval_create_from_date_string('46 days'));
8
        $temp_day    = date_format($date_temp_obj, 'd');
9
        $temp_month  = date_format($date_temp_obj, 'm');
10
    echo "\nAschermittwoch:\t" .  $current_year . "-" . $temp_month . "-" . $temp_day;
11
    // calculation OK
12
13
// KARFREITAG:      2 days before before Easter Sunday
14
    $date_temp_obj = $date_easter;
15
    date_sub($date_temp_obj, date_interval_create_from_date_string('2 days'));
16
        $temp_day    = date_format($date_temp_obj, 'd');
17
        $temp_month  = date_format($date_temp_obj, 'm');
18
    echo "\nKarfreitag:\t" .  $current_year . "-" . $temp_month . "-" . $temp_day;
19
    // calculation WRONG! Aschermittwoch - 2 days
20
...

Hier das vollständige Programm in der Sandbox: 
https://sandbox.onlinephpfunctions.com/code/0f965cf07997bfd4c19d5961c0e1ec30df7c2f6d

Resultat:
1
THIS DONT WORK :-(
2
3
Ostersonntag:  2021-4-4
4
5
Aschermittwoch:  2021-02-17
6
Karfreitag:  2021-02-15
7
Himmelfahrt:  2021-03-26
8
Pfingsten:  2021-05-14

Wie Sie sehen, wird der Aschermittwoch beide Male korrekt berechnet, der 
Karfreitag wird dann aber auf den Aschermittwoch referenziert (2 Tage 
davor) statt auf den Ostersonntag aus /$date_easter/. Und so setzt sich 
das fort: Himmelfahrt wird auf diesen falschen Karfreitag referenziert 
(+39 Tage). Bei Pfingsten bin ich dann von allen guten Geistern 
verlassen: +40 Tage auf Ostersonntag bzw. +49 Tage auf die falsche 
Himmelfahrt.

Ich habe das Gefühl, dass der Befehl
1
$date_temp_obj = $date_easter;
gar nicht funktioniert, sonder mit dem alten date_temp_obj  
weiterarbeitet.

Aber ich verstehe nicht, warum? Wie kann ich das beheben?
Der Workaround mit jedes mal neu /date_create(.)/ aufrufen funktioniert 
zwar, ist aber unschön. Ich hoffe, Sie verstehen mein Anliegen.

Könnte mir bitte jemand erklären, was der Fehler ist.
Vielen Dank im Voraus!

Beste Grüße, Marek

[1] https://www.php.net/manual/de/function.easter-days.php
[2] http://www.othmar.at/aktuell/ostern/osterdatum.html
[2a] http://www.maa.clell.de/StarDate/feiertage.html
[3] https://www.php.net/manual/de/function.date-sub.php
[4] https://www.php.net/manual/de/function.date-add.php
[5] https://www.php.net/manual/de/datetime.sub.php
[6] https://www.php.net/manual/de/datetime.add.php
[7] https://www.php.net/manual/de/function.date-create.php

P.S. Auf meinem Server läuft PHP-Version 7.0.4. aber auch mit neueren 
Versionen in der Sandbox lässt sich das Fehlverhalten reproduzieren.

von Zombie (Gast)


Lesenswert?

soweit ich mich erinnere (lang, lang ist's her), müssen objekte explizit 
geklont werden:
1
$date_temp_obj = clone $date_easter;

von Dirk K. (merciless)


Lesenswert?

Marek N. schrieb:
>
1
> $date_temp_obj = $date_easter;
2
>
Da wird wohl nicht das Objekt kopiert, sondern
nur eine Referenz ("Zeiger") darauf. Du veränderst
dann das originale Objekt.

merciless

von Marek N. (Gast)


Lesenswert?

Tut mir Leid, das verstehe ich nich :-(

Wenn ich rechne
1
<?php
2
$a = 1;
3
$b = 2;
4
5
$a_bkp = $a;
6
$a = 1000;
7
8
$c = $a_bkp + $b;   
9
10
echo $c; // korrektes Ergebnis: 3

Da ist es doch egal, ob ich meine Variable $a_bkp oder $a_obj oder 
$a_zombie nenne.

von Dirk K. (merciless)


Lesenswert?

Marek N. schrieb:
> Tut mir Leid, das verstehe ich nich :-(
>
> Wenn ich rechne
>
1
> <?php
2
> $a = 1;
3
> $b = 2;
4
> 
5
> $a_bkp = $a;
6
> $a = 1000;
7
> 
8
> $c = $a_bkp + $b;
9
> 
10
> echo $c; // korrektes Ergebnis: 3
11
>
>
> Da ist es doch egal, ob ich meine Variable $a_bkp oder $a_obj oder
> $a_zombie nenne.
Da verwendest du auch keine Objekte einer Klasse,
sondern einen "atomaren" Datentyp.

https://stackoverflow.com/questions/185934/how-do-i-create-a-copy-of-an-object-in-php

merciless

: Bearbeitet durch User
von 🐧 DPA 🐧 (Gast)


Lesenswert?

Wenn $a ein Objekt wäre, würde das hier passieren:
1
<?php
2
$a = (object)['v'=>1];
3
$b = 2;
4
5
$a_bkp = $a;
6
$a->v = 1000;
7
8
$c = $a_bkp->v + $b;
9
10
echo $c; // 1002

btw.
1
<?php
2
$a = 1;
3
$b = 2;
4
5
$a_bkp = &$a;
6
$a = 1000;
7
8
$c = $a_bkp + $b;
9
10
echo $c; // 1002

von 🐧 DPA 🐧 (Gast)


Lesenswert?


Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.