[3.0] Let SMF\Time do arithmetic with an SMF\TimeInterval - #9405
[3.0] Let SMF\Time do arithmetic with an SMF\TimeInterval#9405albertlast wants to merge 2 commits into
Conversation
The calendar is a fatal error on a stock install. Not on a forum that has had an event added to it: on every forum, because the installer seeds 28 holidays and drawing any of them goes through Event::__set(), which does (clone $this->start)->add($this->duration) with a TimeInterval. PHP's date arithmetic reads a \DateInterval's internal state directly rather than through the property hooks, and TimeInterval deliberately never calls parent::__construct(), so \DateTime::add() rejects it outright: Object of type SMF\TimeInterval (inheriting DateInterval) has not been correctly initialized by calling parent::__construct() in its constructor TimeInterval keeps its values in a private \DateInterval instead, because that is the only way to give the class a working $days property; calling parent::__construct() makes PHP serve every one of those properties from the internal state and ignore the hooks, so $days goes back to being false. Rather than give that up, TimeInterval now hands out the instance it is wrapping, and SMF\Time::add() and ::sub() unwrap before calling PHP. Every calendar caller goes through SMF\Time, so they all keep working, and a plain \DateInterval takes the same path as before. The calendar renders again, holidays and all, with nothing in the error log. Signed-off-by: Mathias Papenbrock <mathiaspapealbert@hotmail.com> Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
RecurrenceIterator holds its view start as a plain \DateTime or \DateTimeImmutable, whichever the recurrence type calls for, so it does not get the unwrapping that SMF\Time::add() does. Adding a view duration there threw the same error, which is why posting a calendar event still failed. Signed-off-by: Mathias Papenbrock <mathiaspapealbert@hotmail.com> Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
|
Pushed a second commit: the first one was incomplete.
Found by re-sweeping the calendar's own pages after the first fix rather than stopping at This is the leak in fixing it at the boundary rather than in the type, and it is worth being plain about: every Separately, |
|
We're not going to use this solution. I will submit a PR soon that restores full compatibility of SMF\TimeInterval with \DateInterval. |
Description
The calendar is a fatal error on a stock install. Not on a forum that has had an event added to it — on every forum, because the installer seeds 28 holidays, and drawing any of them goes through
Calendar\Event::__set():$this->durationis anSMF\TimeInterval, and PHP refuses it:This is the bug reported in #9384. It has been sitting there since, so here is a fix that does not require settling the design question first.
Why the constructor is not simply restored
PHP's date arithmetic reads a
\DateInterval's internal state directly, not the property hooks.TimeIntervalkeeps its values in a private\DateIntervaland serves them through hooks precisely so that$dayscan work — as its own docblock says, that is the only reliable way to get a usable$daysin a subclass.Calling
parent::__construct()fixes the arithmetic and breaks that, because PHP then serves every hooked property from the internal state and ignores the hooks. Confirmed on the image's PHP 8.4.24:Assigning
$this->daysafterwards does not help either — it creates a deprecated dynamic property that shadows nothing, and reads still returnfalse.Calendar\Holiday.php:214reads->daysoff aTime::diff()result, so losing it is not free.What this does instead
TimeInterval::toDateInterval()hands out the instance it is wrapping, andSMF\Time::add()/::sub()unwrap before calling PHP:Every caller that passes a
TimeInterval—Event.php×2,EventOccurrence.php×3,Actions/Calendar.php— has anSMF\Timeon the left-hand side, so they are all covered. A plain\DateIntervaltakes exactly the path it took before.TimeInterval's public API is unchanged, and$daysstill works.It is a fix at the boundary rather than in the type, and I would rather you had the choice: the alternative is to make
TimeIntervala genuine\DateIntervaland give up the hooked$days, which changes a public property's behaviour and is your call, not mine. This unblocks the calendar either way.Checked
On the running forum, before:
action=calendarreturns the fatal error page,Event.php:1125insmf_log_errors. After: renders, and December 2026 marks the 21st and the 25th asclass="days windowbg holidays", November the 11th and the 26th. Nothing new in the error log.Rebased on the merged #9383 — the fractional duration fix — which this needs to construct the seeded holidays in the first place.
Issues References (Fixes|Related|Closes)
Fixes #9384
Related to #7933