briannesbitt / Carbon

A simple PHP API extension for DateTime.
https://carbon.nesbot.com/
MIT License
16.53k stars 1.28k forks source link

Error "zend_mm_heap corrupted" when using Carbon 2 alongside Timecop PECL package #2756

Closed coopan closed 1 year ago

coopan commented 1 year ago

Hello,

I encountered an issue with the following code. Note that we use the Timecop PECL library that takes control over PHP date and time functions and this has been living fine with Carbon 1:

$now = 1572318476;
\timecop_freeze($now);
$fixedTime = Carbon::createFromTimestamp($now);
Carbon::setTestNow($fixedTime);

Carbon version: 2.65.0

PHP version: 7.4.32

I expected to get:

No error

But I actually get:

zend_mm_heap corrupted

The example is in a unit test where we are using Timecop to freeze time. We are upgrading Carbon from 1.x to 2.x as we head towards a PHP 8 upgrade, but we found that as soon as we go from Carbon 1 to 2, we see these fatal zend_mm_heap corrupted errors. The above is only an example. It appears that any code a test touches that uses Carbon while Timecop has a freeze in place will result in the same issue. The exact same code is fine with Carbon 1. So I was wondering if anyone might know the cause of this error specifically when Carbon 2 is working alongside Timecop?

We will be removing Timecop as part of the upgrade, but we are trying to separate concerns. For now understanding the issue so we can develop a workaround would be great.

Thanks!

kylekatarnls commented 1 year ago

Hello,

Honestly, I don't know, without a stack trace, it's a blind search.

May I ask why would you use Carbon::setTestNow() if time is already frozen/changed in test using timecop_freeze?

It's redundant since timecop already mock the time and when a new Carbon object is created it will use this mocked time.

coopan commented 1 year ago

Hi. Yeah it's a strange error. Doesn't present a trace at all. I took some time yesterday to manually step it into Carbon to see where it's being triggered. I managed to trap it at phpunit_bridge (I did wonder if this was a PHPUnit thing but we're on latest)

Single stack reported: https://github.com/briannesbitt/Carbon/blob/master/src/Carbon/Traits/Creator.php#L87 Message: date_default_timezone_set(): Timezone ID '+00:00' is invalid

So looks like Carbon 2 does something different with TZ that Carbon 1 didn't?

Code in this case:

\timecop_freeze(86405);
$time = Carbon::createFromTimestamp(86405);

setTestNow() doesn't play apart. This just happens creating a Carbon object.

To answer your question, we don't. The above was just an example to easily reproduce on the off chance someone was using or familiar with Timecop. The real world problem is we might not have Carbon::setTestNow() at all on the test and just using Timecop. But if the code that the test hits uses Carbon, we get this error.

coopan commented 1 year ago

Even expanding on the above and setting the timezone at PHP-level results in the same issue:

date_default_timezone_set('UTC');
$this->timeFreeze(86405);
$time = Carbon::createFromTimestamp(86405);

Looks like timecop does something pokey with timezone, and Carbon 2 doesn't like it.

kylekatarnls commented 1 year ago

Hello,

I just installed timecop locally and tried your code above, it works fine and does not hit zend_mm_heap corrupted.

I only get this notice: from \timecop_freeze():

Notice: date_default_timezone_set(): Timezone ID '+00:00' is invalid

Which is an issue in timecop, but it does not prevent the time to be mocked properly.

So using \timecop_freeze() of Timecop 1.2.10 on a thred-safe PHP 7.4 install + Carbon::setTestNow() from latest Carbon version is not incompatible, you have an other ingredient in the mix needed to reproduce the bug.

I you can provide a standalone VM/docker/sandbox with the minimum of dependencies and code to reproduce, I would re-open this issue.