Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 113 additions & 24 deletions ext/calendar/EasterDateJitHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,38 @@
/**
* easter_date() for compiled JIT/AOT modules (#27356, php-in-PHP).
*
* NestedJIT-safe: easter_days math + UTC midnight via jdtounix/gregorianToJd
* (no VmDate::mktime). Matches php-src easter.c local midnight when TZ=UTC.
* SSOT on the VM path: {@see VmCalendar::easterDate()}
* NestedJIT leaf: inlined easter_days + gregorian SDN + jdtounix. No throws —
* ValueError NestedJIT fails module verify ("Referring to a basic block in another
* function" / throw_uncaught) and blocks helper-runtime refresh #24302. VM wrappers
* still validate years before calling.
* php-src: ext/calendar/easter.c — PHP_FUNCTION(easter_date)
*/
final class EasterDateJitHelper
{
private const UNIX_EPOCH_JD = 2440588;

private const SECS_PER_DAY = 86400;

private const GREGOR_SDN_OFFSET = 32045;

private const DAYS_PER_400_YEARS = 146097;

private const DAYS_PER_4_YEARS = 1461;

private const DAYS_PER_5_MONTHS = 153;

private const EASTER_ROMAN = 1;

private const EASTER_ALWAYS_GREGORIAN = 2;

private const EASTER_ALWAYS_JULIAN = 3;

public static function easterDateArgv(int $year, int $mode): int
{
self::assertEasterYear($year);
$easter = VmCalendar::easterDays($year, $mode);
if ($year < 1970) {
return 0;
}
$easter = self::easterDays($year, $mode);
if ($easter < 11) {
$month = 3;
$day = $easter + 21;
Expand All @@ -26,39 +47,107 @@ public static function easterDateArgv(int $year, int $mode): int
$day = $easter - 10;
}

return VmCalendar::jdtounix(VmCalendar::gregorianToJd($month, $day, $year));
return self::jdtounix(self::gregorianToSdn($year, $month, $day));
}

/** Omitted/null $year — local calendar year (php-src easter.c). */
public static function easterDateNowArgv(int $mode): int
{
return self::easterDateArgv(self::currentYear(), $mode);
}

public static function currentYear(): int
{
return (int) \date('Y');
$days = intdiv(\time(), 86400);
$z = $days + 719468;
$era = intdiv($z >= 0 ? $z : $z - 146096, 146097);
$doe = $z - $era * 146097;
$yoe = intdiv($doe - intdiv($doe, 1460) + intdiv($doe, 36524) - intdiv($doe, 146096), 365);

return $yoe + $era * 400;
}

private static function assertEasterYear(int $year): void
private static function easterDays(int $year, int $method): int
{
$maxYear = intdiv(\PHP_INT_MAX, 5) * 4;
if ($year <= 0 || $year > $maxYear) {
throw new \ValueError(
\sprintf('easter_date(): Argument #1 ($year) must be between 1 and %d', $maxYear)
);
}
if (\PHP_INT_SIZE >= 8) {
if ($year < 1970) {
throw new \ValueError('easter_date(): Argument #1 ($year) must be a year after 1970 (inclusive)');
$golden = ($year % 19) + 1;

if (($year <= 1582 && self::EASTER_ALWAYS_GREGORIAN !== $method)
|| ($year >= 1583 && $year <= 1752
&& self::EASTER_ROMAN !== $method
&& self::EASTER_ALWAYS_GREGORIAN !== $method)
|| self::EASTER_ALWAYS_JULIAN === $method) {
$dom = ($year + intdiv($year, 4) + 5) % 7;
if ($dom < 0) {
$dom += 7;
}
$pfm = (3 - (11 * $golden) - 7) % 30;
if ($pfm < 0) {
$pfm += 30;
}
} else {
$dom = ($year + intdiv($year, 4) - intdiv($year, 100) + intdiv($year, 400)) % 7;
if ($dom < 0) {
$dom += 7;
}
if ($year > 2000000000) {
throw new \ValueError(
'easter_date(): Argument #1 ($year) must be a year before 2.000.000.000 (inclusive)'
);
$solar = intdiv($year - 1600, 100) - intdiv($year - 1600, 400);
$lunar = intdiv(intdiv($year - 1400, 100) * 8, 25);
$pfm = (3 - (11 * $golden) + $solar - $lunar) % 30;
if ($pfm < 0) {
$pfm += 30;
}
} elseif ($year < 1970 || $year > 2037) {
throw new \ValueError('easter_date(): Argument #1 ($year) must be between 1970 and 2037 (inclusive)');
}

if (29 === $pfm || (28 === $pfm && $golden > 11)) {
--$pfm;
}

$tmp = (4 - $pfm - $dom) % 7;
if ($tmp < 0) {
$tmp += 7;
}

return (int) ($pfm + $tmp + 1);
}

private static function gregorianToSdn(int $inputYear, int $inputMonth, int $inputDay): int
{
if (0 === $inputYear || $inputYear < -4714
|| $inputMonth <= 0 || $inputMonth > 12
|| $inputDay <= 0 || $inputDay > 31) {
return 0;
}
if (-4714 === $inputYear) {
if ($inputMonth < 11 || (11 === $inputMonth && $inputDay < 25)) {
return 0;
}
}

if ($inputYear < 0) {
$year = $inputYear + 4801;
} else {
$year = $inputYear + 4800;
}

if ($inputMonth > 2) {
$month = $inputMonth - 3;
} else {
$month = $inputMonth + 9;
--$year;
}

return intdiv(intdiv($year, 100) * self::DAYS_PER_400_YEARS, 4)
+ intdiv(($year % 100) * self::DAYS_PER_4_YEARS, 4)
+ intdiv($month * self::DAYS_PER_5_MONTHS + 2, 5)
+ $inputDay
- self::GREGOR_SDN_OFFSET;
}

private static function jdtounix(int $julianDay): int
{
$maxJd = self::UNIX_EPOCH_JD + intdiv(\PHP_INT_MAX, self::SECS_PER_DAY);
if ($julianDay < self::UNIX_EPOCH_JD || $julianDay > $maxJd) {
return 0;
}

return ($julianDay - self::UNIX_EPOCH_JD) * self::SECS_PER_DAY;
}
}
72 changes: 60 additions & 12 deletions ext/calendar/EasterDaysJitHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,38 +5,86 @@
namespace PHPCompiler\ext\calendar;

/**
* easter_days() for compiled JIT/AOT modules (#27358, php-in-PHP).
* easter_days() for compiled JIT/AOT modules (#27358).
*
* SSOT: {@see VmCalendar::easterDays()}
* NestedJIT leaf — no throws (ValueError NestedJIT fails module verify with
* "Referring to a basic block in another function" / throw_uncaught; blocks
* helper-runtime refresh #24302). Invalid years return 0; VM wrapper still
* validates before call.
* php-src: ext/calendar/easter.c — PHP_FUNCTION(easter_days)
*/
final class EasterDaysJitHelper
{
private const EASTER_ROMAN = 1;

private const EASTER_ALWAYS_GREGORIAN = 2;

private const EASTER_ALWAYS_JULIAN = 3;

public static function easterDaysArgv(int $year, int $mode): int
{
self::assertEasterYear($year);
if ($year <= 0) {
return 0;
}

return VmCalendar::easterDays($year, $mode);
return self::easterDays($year, $mode);
}

/** Omitted/null $year — local calendar year (php-src easter.c). */
public static function easterDaysNowArgv(int $mode): int
{
return self::easterDaysArgv(self::currentYear(), $mode);
}

public static function currentYear(): int
{
return (int) \date('Y');
$days = intdiv(\time(), 86400);
$z = $days + 719468;
$era = intdiv($z >= 0 ? $z : $z - 146096, 146097);
$doe = $z - $era * 146097;
$yoe = intdiv($doe - intdiv($doe, 1460) + intdiv($doe, 36524) - intdiv($doe, 146096), 365);

return $yoe + $era * 400;
}

private static function assertEasterYear(int $year): void
private static function easterDays(int $year, int $method): int
{
$maxYear = intdiv(\PHP_INT_MAX, 5) * 4;
if ($year <= 0 || $year > $maxYear) {
throw new \ValueError(
\sprintf('easter_days(): Argument #1 ($year) must be between 1 and %d', $maxYear)
);
$golden = ($year % 19) + 1;

if (($year <= 1582 && self::EASTER_ALWAYS_GREGORIAN !== $method)
|| ($year >= 1583 && $year <= 1752
&& self::EASTER_ROMAN !== $method
&& self::EASTER_ALWAYS_GREGORIAN !== $method)
|| self::EASTER_ALWAYS_JULIAN === $method) {
$dom = ($year + intdiv($year, 4) + 5) % 7;
if ($dom < 0) {
$dom += 7;
}
$pfm = (3 - (11 * $golden) - 7) % 30;
if ($pfm < 0) {
$pfm += 30;
}
} else {
$dom = ($year + intdiv($year, 4) - intdiv($year, 100) + intdiv($year, 400)) % 7;
if ($dom < 0) {
$dom += 7;
}
$solar = intdiv($year - 1600, 100) - intdiv($year - 1600, 400);
$lunar = intdiv(intdiv($year - 1400, 100) * 8, 25);
$pfm = (3 - (11 * $golden) + $solar - $lunar) % 30;
if ($pfm < 0) {
$pfm += 30;
}
}

if (29 === $pfm || (28 === $pfm && $golden > 11)) {
--$pfm;
}

$tmp = (4 - $pfm - $dom) % 7;
if ($tmp < 0) {
$tmp += 7;
}

return (int) ($pfm + $tmp + 1);
}
}
12 changes: 12 additions & 0 deletions lib/JIT/Builtin/DefaultTimezoneCivilRuntime.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,18 @@ final class DefaultTimezoneCivilRuntime

private const TZ_HELPER_PATH = '/ext/standard/DefaultTimezoneJitHelper.php';

/**
* Civil NestedJIT reads DefaultTimezoneJitHelper statics — emit-helper --unit=
* must NestedJIT both PHP files in one TU or assertCompiledHelpersPresent fails
* (#24302 stale refresh / peer PackJitHelper HELPER_BUNDLE).
*
* @var list<string>
*/
private const HELPER_BUNDLE = [
self::TZ_HELPER_PATH,
self::HELPER_PATH,
];

private const CIVIL_HELPER = 'PHPCompiler\\ext\\standard\\DefaultTimezoneCivilJitHelper::localCivilTimestamp';

private const IS_DST_HELPER = 'PHPCompiler\\ext\\standard\\DefaultTimezoneCivilJitHelper::localIsDst';
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"fingerprint":"a43823566cdaedc7e3f8","fingerprint_version":2,"unit":"/ext/calendar/EasterDateJitHelper.php","deps":["/ext/calendar/EasterDateJitHelper.php","/ext/calendar/VmCalendar.php","/ext/standard/DefaultTimezoneCivilJitHelper.php","/ext/standard/VmDate.php","/ext/standard/VmDateTimeNative.php"],"helpers":{"phpcompiler\\ext\\calendar\\easterdatejithelper::easterdateargv":"PHPCompiler_ext_calendar_EasterDateJitHelper__easterdateargv","phpcompiler\\ext\\calendar\\easterdatejithelper::easterdatenowargv":"PHPCompiler_ext_calendar_EasterDateJitHelper__easterdatenowargv"},"init_symbol":"__init__unit_ext_calendar_EasterDateJitHelper_php","shutdown_symbol":"__shutdown__unit_ext_calendar_EasterDateJitHelper_php","init_via_global_ctor":true,"runtime_safe":true}
{"fingerprint":"dccc36f32bbfc62fea7b","fingerprint_version":2,"unit":"/ext/calendar/EasterDateJitHelper.php","deps":["/ext/calendar/EasterDateJitHelper.php"],"helpers":{"phpcompiler\\ext\\calendar\\easterdatejithelper::easterdateargv":"PHPCompiler_ext_calendar_EasterDateJitHelper__easterdateargv","phpcompiler\\ext\\calendar\\easterdatejithelper::easterdatenowargv":"PHPCompiler_ext_calendar_EasterDateJitHelper__easterdatenowargv"},"init_symbol":"__init__unit_ext_calendar_EasterDateJitHelper_php","shutdown_symbol":"__shutdown__unit_ext_calendar_EasterDateJitHelper_php","init_via_global_ctor":true,"runtime_safe":true}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"fingerprint":"e0a0d325df6ef261cd15","fingerprint_version":2,"unit":"/ext/calendar/EasterDaysJitHelper.php","deps":["/ext/calendar/EasterDaysJitHelper.php","/ext/calendar/VmCalendar.php","/ext/standard/DefaultTimezoneCivilJitHelper.php","/ext/standard/VmDate.php","/ext/standard/VmDateTimeNative.php"],"helpers":{"phpcompiler\\ext\\calendar\\easterdaysjithelper::easterdaysargv":"PHPCompiler_ext_calendar_EasterDaysJitHelper__easterdaysargv","phpcompiler\\ext\\calendar\\easterdaysjithelper::easterdaysnowargv":"PHPCompiler_ext_calendar_EasterDaysJitHelper__easterdaysnowargv"},"init_symbol":"__init__unit_ext_calendar_EasterDaysJitHelper_php","shutdown_symbol":"__shutdown__unit_ext_calendar_EasterDaysJitHelper_php","init_via_global_ctor":true,"runtime_safe":true}
{"fingerprint":"653fb27fafcbd849dc21","fingerprint_version":2,"unit":"/ext/calendar/EasterDaysJitHelper.php","deps":["/ext/calendar/EasterDaysJitHelper.php"],"helpers":{"phpcompiler\\ext\\calendar\\easterdaysjithelper::easterdaysargv":"PHPCompiler_ext_calendar_EasterDaysJitHelper__easterdaysargv","phpcompiler\\ext\\calendar\\easterdaysjithelper::easterdaysnowargv":"PHPCompiler_ext_calendar_EasterDaysJitHelper__easterdaysnowargv"},"init_symbol":"__init__unit_ext_calendar_EasterDaysJitHelper_php","shutdown_symbol":"__shutdown__unit_ext_calendar_EasterDaysJitHelper_php","init_via_global_ctor":true,"runtime_safe":true}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"fingerprint":"48a434f0ff07d56f481a","fingerprint_version":2,"unit":"/ext/dom/DomC14NJitHelper.php","deps":["/ext/dom/DomC14NJitHelper.php","/ext/dom/DomClassConstants.php","/ext/dom/DomConstants.php","/ext/dom/DomExceptionConstants.php","/ext/dom/DomHtmlElementPropertySupport.php","/ext/dom/DomLiveNamedNodeMapIterator.php","/ext/dom/DomLiveNodeListIterator.php","/ext/dom/DomLivingConstants.php","/ext/dom/DomNodeState.php","/ext/dom/DomRegistry.php","/ext/dom/JitDomLoadXMLUserScript.php","/ext/dom/VmDom.php","/ext/dom/VmDomJitFrame.php","/ext/dom/VmDomLiving.php","/ext/dom/VmDomSimpleXmlBridge.php","/ext/dom/VmDomTokenList.php","/ext/dom/VmDomValidationNative.php","/ext/dom/VmDomXPath.php"],"helpers":{"phpcompiler\\ext\\dom\\domc14njithelper::c14nfileargv":"PHPCompiler_ext_dom_DomC14NJitHelper__c14nfileargv","phpcompiler\\ext\\dom\\domc14njithelper::c14nargv":"PHPCompiler_ext_dom_DomC14NJitHelper__c14nargv"},"init_symbol":"__init__unit_ext_dom_DomC14NJitHelper_php","shutdown_symbol":"__shutdown__unit_ext_dom_DomC14NJitHelper_php","init_via_global_ctor":true,"runtime_safe":true}
{"fingerprint":"b6f6051fdaea58990b13","fingerprint_version":2,"unit":"/ext/dom/DomC14NJitHelper.php","deps":["/ext/dom/DomC14NJitHelper.php","/ext/dom/DomClassConstants.php","/ext/dom/DomConstants.php","/ext/dom/DomExceptionConstants.php","/ext/dom/DomHtmlElementPropertySupport.php","/ext/dom/DomLiveNamedNodeMapIterator.php","/ext/dom/DomLiveNodeListIterator.php","/ext/dom/DomLivingConstants.php","/ext/dom/DomNodeState.php","/ext/dom/DomRegistry.php","/ext/dom/JitDomLoadXMLUserScript.php","/ext/dom/VmDom.php","/ext/dom/VmDomJitFrame.php","/ext/dom/VmDomLiving.php","/ext/dom/VmDomSimpleXmlBridge.php","/ext/dom/VmDomTokenList.php","/ext/dom/VmDomValidationNative.php","/ext/dom/VmDomXPath.php"],"helpers":{"phpcompiler\\ext\\dom\\domc14njithelper::c14nfileargv":"PHPCompiler_ext_dom_DomC14NJitHelper__c14nfileargv","phpcompiler\\ext\\dom\\domc14njithelper::c14nargv":"PHPCompiler_ext_dom_DomC14NJitHelper__c14nargv"},"init_symbol":"__init__unit_ext_dom_DomC14NJitHelper_php","shutdown_symbol":"__shutdown__unit_ext_dom_DomC14NJitHelper_php","init_via_global_ctor":true,"runtime_safe":true}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"fingerprint":"5cda816d89415d819519","fingerprint_version":2,"unit":"/ext/standard/DefaultTimezoneCivilJitHelper.php","deps":["/ext/standard/DefaultTimezoneCivilJitHelper.php","/ext/standard/VmDate.php","/ext/standard/VmDateTimeNative.php"],"helpers":{"phpcompiler\\ext\\standard\\defaulttimezoneciviljithelper::localciviltimestamp":"PHPCompiler_ext_standard_DefaultTimezoneCivilJitHelper__localciviltimestamp","phpcompiler\\ext\\standard\\defaulttimezoneciviljithelper::localisdst":"PHPCompiler_ext_standard_DefaultTimezoneCivilJitHelper__localisdst"},"init_symbol":"__init__unit_ext_standard_DefaultTimezoneCivilJitHelper_php","shutdown_symbol":"__shutdown__unit_ext_standard_DefaultTimezoneCivilJitHelper_php","init_via_global_ctor":true,"runtime_safe":true}
{"fingerprint":"6963a0f3e661263b652f","fingerprint_version":2,"unit":"/ext/standard/DefaultTimezoneCivilJitHelper.php","deps":["/ext/standard/DefaultTimezoneCivilJitHelper.php","/ext/standard/DefaultTimezoneJitHelper.php"],"helpers":{"phpcompiler\\ext\\standard\\defaulttimezoneciviljithelper::localciviltimestamp":"PHPCompiler_ext_standard_DefaultTimezoneCivilJitHelper__localciviltimestamp","phpcompiler\\ext\\standard\\defaulttimezoneciviljithelper::localisdst":"PHPCompiler_ext_standard_DefaultTimezoneCivilJitHelper__localisdst","phpcompiler\\ext\\standard\\defaulttimezoneciviljithelper::formattimezonetoken":"PHPCompiler_ext_standard_DefaultTimezoneCivilJitHelper__formattimezonetoken","phpcompiler\\ext\\standard\\defaulttimezonejithelper::defaulttimezoneget":"PHPCompiler_ext_standard_DefaultTimezoneJitHelper__defaulttimezoneget","phpcompiler\\ext\\standard\\defaulttimezonejithelper::trydefaulttimezoneset":"PHPCompiler_ext_standard_DefaultTimezoneJitHelper__trydefaulttimezoneset","phpcompiler\\ext\\standard\\defaulttimezonejithelper::emitinvalidtimezonenotice":"PHPCompiler_ext_standard_DefaultTimezoneJitHelper__emitinvalidtimezonenotice"},"init_symbol":"__init__unit_ext_standard_DefaultTimezoneCivilJitHelper_php","shutdown_symbol":"__shutdown__unit_ext_standard_DefaultTimezoneCivilJitHelper_php","init_via_global_ctor":true,"runtime_safe":true}
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"fingerprint":"33bb898ef2a3a45fd9eb","fingerprint_version":2,"unit":"/ext/standard/DefaultTimezoneJitHelper.php","deps":["/ext/standard/DefaultTimezoneJitHelper.php","/ext/standard/TriggerErrorJitHelper.php","/ext/standard/VmDate.php"],"helpers":{"phpcompiler\\ext\\standard\\defaulttimezonejithelper::defaulttimezoneget":"PHPCompiler_ext_standard_DefaultTimezoneJitHelper__defaulttimezoneget","phpcompiler\\ext\\standard\\defaulttimezonejithelper::trydefaulttimezoneset":"PHPCompiler_ext_standard_DefaultTimezoneJitHelper__trydefaulttimezoneset","phpcompiler\\ext\\standard\\defaulttimezonejithelper::emitinvalidtimezonenotice":"PHPCompiler_ext_standard_DefaultTimezoneJitHelper__emitinvalidtimezonenotice"},"init_symbol":"__init__unit_ext_standard_DefaultTimezoneJitHelper_php","shutdown_symbol":"__shutdown__unit_ext_standard_DefaultTimezoneJitHelper_php","init_via_global_ctor":true,"runtime_safe":true}
{"fingerprint":"a632ee4b13733a4bb33e","fingerprint_version":2,"unit":"/ext/standard/DefaultTimezoneJitHelper.php","deps":["/ext/standard/DefaultTimezoneJitHelper.php","/ext/standard/TriggerErrorJitHelper.php","/ext/standard/VmDate.php"],"helpers":{"phpcompiler\\ext\\standard\\defaulttimezonejithelper::defaulttimezoneget":"PHPCompiler_ext_standard_DefaultTimezoneJitHelper__defaulttimezoneget","phpcompiler\\ext\\standard\\defaulttimezonejithelper::trydefaulttimezoneset":"PHPCompiler_ext_standard_DefaultTimezoneJitHelper__trydefaulttimezoneset","phpcompiler\\ext\\standard\\defaulttimezonejithelper::emitinvalidtimezonenotice":"PHPCompiler_ext_standard_DefaultTimezoneJitHelper__emitinvalidtimezonenotice"},"init_symbol":"__init__unit_ext_standard_DefaultTimezoneJitHelper_php","shutdown_symbol":"__shutdown__unit_ext_standard_DefaultTimezoneJitHelper_php","init_via_global_ctor":true,"runtime_safe":true}
Binary file not shown.
Loading