Skip to content

Conversation

@vdekrijger
Copy link
Contributor

@vdekrijger vdekrijger commented Jan 8, 2026

TL;DR

Adds PHP 8.5 support by replacing slope-it/clock-mock (which required the uopz extension that doesn't support PHP 8.5) with symfony/clock.

Minimum PHP version bumped to 8.2 since both 8.0 and 8.1 are EOL.

Changes

Dependencies:

  • php: >=8.0>=8.2
  • phpunit/phpunit: ^9.0^11.0
  • Added symfony/clock: ^6.2|^7.0
  • Removed slope-it/clock-mock (required ext-uopz which doesn't support PHP 8.5)
  • Removed overtrue/phplint (unused in the SDK)

CI:

  • PHP matrix: [8.0, 8.1, 8.2, 8.3, 8.4][8.2, 8.3, 8.4, 8.5]
  • Removed uopz extension (no longer needed)
  • Removed deprecated --no-suggest flag

Why these changes?

slope-it/clock-mock depends on the uopz PHP extension, which doesn't support PHP 8.5. We switched to symfony/clock which provides time mocking without requiring any PHP extensions.

Breaking changes

  • Minimum PHP version is now 8.2
    • PHP 8.0 (EOL Nov 2023): Can't support because symfony/clock requires PHP 8.1+
    • PHP 8.1 (EOL Dec 2024): Could support with PHPUnit 10, but it's EOL

Proof of changes:

Made a simple test script (or rather Claude made it):

PHP Output comparison
<?php
/**
 * Side-by-side comparison of slope-it/clock-mock vs symfony/clock
 *
 * To run this comparison:
 *
 * 1. Install PHP 8.2 with uopz extension:
 *    brew install php@8.2
 *    /opt/homebrew/opt/php@8.2/bin/pecl install uopz
 *
 * 2. Create a temp directory and install both libraries:
 *    mkdir /tmp/clock-comparison && cd /tmp/clock-comparison
 *    echo '{"require":{"symfony/clock":"^7.0","slope-it/clock-mock":"^0.4.0"}}' > composer.json
 *    /opt/homebrew/opt/php@8.2/bin/php /opt/homebrew/bin/composer install
 *
 * 3. Copy this file to that directory and run:
 *    cp test_clock_comparison.php /tmp/clock-comparison/
 *    cd /tmp/clock-comparison
 *    /opt/homebrew/opt/php@8.2/bin/php test_clock_comparison.php
 */

require_once "vendor/autoload.php";

use SlopeIt\ClockMock\ClockMock;
use Symfony\Component\Clock\Clock;
use Symfony\Component\Clock\NativeClock;
use Symfony\Component\Clock\MockClock;

echo "╔══════════════════════════════════════════════════════════════════════════════╗\n";
echo "║  SIDE-BY-SIDE: slope-it/clock-mock vs symfony/clock                          ║\n";
echo "║  PHP " . PHP_VERSION . " with uopz extension                                         ║\n";
echo "╚══════════════════════════════════════════════════════════════════════════════╝\n\n";

// ============================================================================
// PART A: NORMAL OPERATION - Verify old and new code produce same results
// ============================================================================
echo "╔══════════════════════════════════════════════════════════════════════════════╗\n";
echo "║  PART A: NORMAL OPERATION - Old vs New code (no mocking)                     ║\n";
echo "╚══════════════════════════════════════════════════════════════════════════════╝\n\n";

Clock::set(new NativeClock()); // Ensure real clock

echo "┌──────────────────────────────────────────────────────────────────────────────┐\n";
echo "│ Change #1: \$ts = time();                                                    │\n";
echo "│         → \$ts = Clock::get()->now()->getTimestamp();                        │\n";
echo "└──────────────────────────────────────────────────────────────────────────────┘\n";
$old1 = time();
$new1 = Clock::get()->now()->getTimestamp();
echo "  OLD: time()                              = " . $old1 . " (" . date("c", $old1) . ")\n";
echo "  NEW: Clock::get()->now()->getTimestamp() = " . $new1 . " (" . date("c", $new1) . ")\n";
echo "  ✅ EQUIVALENT: " . (abs($old1 - $new1) <= 1 ? "YES" : "NO") . "\n\n";

echo "┌──────────────────────────────────────────────────────────────────────────────┐\n";
echo "│ Change #2: \$parsedDt = new \\DateTime(\"now\", new \\DateTimeZone(\"UTC\"));      │\n";
echo "│         → \$parsedDt = \\DateTime::createFromInterface(Clock::get()->now())   │\n";
echo "│                        ->setTimezone(new \\DateTimeZone(\"UTC\"));              │\n";
echo "└──────────────────────────────────────────────────────────────────────────────┘\n";
$old2 = new \DateTime("now", new \DateTimeZone("UTC"));
$new2 = \DateTime::createFromInterface(Clock::get()->now())->setTimezone(new \DateTimeZone("UTC"));
echo "  OLD: new DateTime(\"now\", UTC)                        = " . $old2->format("Y-m-d H:i:s T") . "\n";
echo "  NEW: DateTime::createFromInterface(Clock::...)->UTC  = " . $new2->format("Y-m-d H:i:s T") . "\n";
echo "  ✅ EQUIVALENT: " . (abs($old2->getTimestamp() - $new2->getTimestamp()) <= 1 ? "YES" : "NO") . "\n\n";

echo "┌──────────────────────────────────────────────────────────────────────────────┐\n";
echo "│ Change #3: return date(\"c\");                                                │\n";
echo "│         → return date(\"c\", Clock::get()->now()->getTimestamp());            │\n";
echo "└──────────────────────────────────────────────────────────────────────────────┘\n";
$old3 = date("c");
$new3 = date("c", Clock::get()->now()->getTimestamp());
echo "  OLD: date(\"c\")                                       = " . $old3 . "\n";
echo "  NEW: date(\"c\", Clock::get()->now()->getTimestamp())  = " . $new3 . "\n";
echo "  ✅ EQUIVALENT: " . ($old3 === $new3 ? "YES" : "YES (within 1 sec)") . "\n\n";

echo "┌──────────────────────────────────────────────────────────────────────────────┐\n";
echo "│ VERIFICATION: Prove uopz is NOT intercepting in Part A                      │\n";
echo "└──────────────────────────────────────────────────────────────────────────────┘\n";
// Get system time via shell (completely bypasses PHP/uopz)
$systemTime = (int) trim(shell_exec("date +%s"));
$phpTime = time();
$clockTime = Clock::get()->now()->getTimestamp();
echo "  System time (shell 'date +%s'): " . $systemTime . " (" . date("Y-m-d H:i:s", $systemTime) . ")\n";
echo "  PHP time():                     " . $phpTime . " (" . date("Y-m-d H:i:s", $phpTime) . ")\n";
echo "  Clock::get()->now():            " . $clockTime . " (" . date("Y-m-d H:i:s", $clockTime) . ")\n";
echo "  ✅ All three match real time: " . (abs($systemTime - $phpTime) <= 1 && abs($systemTime - $clockTime) <= 1 ? "YES (uopz not interfering)" : "NO") . "\n\n";

// ============================================================================
// PART B: MOCKED TIME - Verify both libraries produce same mocked results
// ============================================================================
echo "╔══════════════════════════════════════════════════════════════════════════════╗\n";
echo "║  PART B: MOCKED TIME - slope-it vs symfony (mocking 2022-05-01 12:00:00)     ║\n";
echo "╚══════════════════════════════════════════════════════════════════════════════╝\n\n";

$mockDate = "2022-05-01 12:00:00";

echo "┌──────────────────────────────────────────────────────────────────────────────┐\n";
echo "│ TEST 1: time() / Clock::get()->now()->getTimestamp()                        │\n";
echo "└──────────────────────────────────────────────────────────────────────────────┘\n";

echo "slope-it/clock-mock (uses uopz to intercept native time()):\n";
$slopeResult1 = null;
ClockMock::executeAtFrozenDateTime(new \DateTime($mockDate), function () use (&$slopeResult1) {
    $slopeResult1 = time();
});
echo "  time() inside ClockMock = " . $slopeResult1 . " (" . date("c", $slopeResult1) . ")\n\n";

echo "symfony/clock (requires explicit Clock::get()->now()):\n";
Clock::set(new MockClock(new \DateTimeImmutable($mockDate)));
$symfonyResult1 = Clock::get()->now()->getTimestamp();
Clock::set(new NativeClock());
echo "  Clock::get()->now()->getTimestamp() = " . $symfonyResult1 . " (" . date("c", $symfonyResult1) . ")\n\n";

echo "  ✅ MATCH: " . ($slopeResult1 === $symfonyResult1 ? "YES" : "NO") . "\n\n";

echo "┌──────────────────────────────────────────────────────────────────────────────┐\n";
echo "│ TEST 2: new DateTime(\"now\") / DateTime::createFromInterface(Clock::...)     │\n";
echo "└──────────────────────────────────────────────────────────────────────────────┘\n";

echo "slope-it/clock-mock:\n";
$slopeResult2 = null;
ClockMock::executeAtFrozenDateTime(new \DateTime($mockDate, new \DateTimeZone("UTC")), function () use (&$slopeResult2) {
    $slopeResult2 = (new \DateTime("now", new \DateTimeZone("UTC")))->format("c");
});
echo "  new DateTime(\"now\", UTC)->format(\"c\") = " . $slopeResult2 . "\n\n";

echo "symfony/clock:\n";
Clock::set(new MockClock(new \DateTimeImmutable($mockDate, new \DateTimeZone("UTC"))));
$symfonyResult2 = \DateTime::createFromInterface(Clock::get()->now())->setTimezone(new \DateTimeZone("UTC"))->format("c");
Clock::set(new NativeClock());
echo "  DateTime::createFromInterface(...)->format(\"c\") = " . $symfonyResult2 . "\n\n";

echo "  ✅ MATCH: " . ($slopeResult2 === $symfonyResult2 ? "YES" : "NO") . "\n\n";

echo "┌──────────────────────────────────────────────────────────────────────────────┐\n";
echo "│ TEST 3: date(\"c\") / date(\"c\", Clock::get()->now()->getTimestamp())          │\n";
echo "└──────────────────────────────────────────────────────────────────────────────┘\n";

echo "slope-it/clock-mock:\n";
$slopeResult3 = null;
ClockMock::executeAtFrozenDateTime(new \DateTime($mockDate, new \DateTimeZone("UTC")), function () use (&$slopeResult3) {
    $slopeResult3 = date("c");
});
echo "  date(\"c\") = " . $slopeResult3 . "\n\n";

echo "symfony/clock:\n";
Clock::set(new MockClock(new \DateTimeImmutable($mockDate, new \DateTimeZone("UTC"))));
$symfonyResult3 = date("c", Clock::get()->now()->getTimestamp());
Clock::set(new NativeClock());
echo "  date(\"c\", Clock::get()->now()->getTimestamp()) = " . $symfonyResult3 . "\n\n";

echo "  ✅ MATCH: " . ($slopeResult3 === $symfonyResult3 ? "YES" : "NO") . "\n\n";

// ============================================================================
// PART C: TIMEZONE TESTS - Verify behavior across different timezones
// ============================================================================
echo "╔══════════════════════════════════════════════════════════════════════════════╗\n";
echo "║  PART C: TIMEZONE TESTS - Verify consistent behavior across timezones       ║\n";
echo "╚══════════════════════════════════════════════════════════════════════════════╝\n\n";

$testTimezones = ['UTC', 'America/New_York', 'Europe/Amsterdam', 'Asia/Tokyo'];
$originalTz = date_default_timezone_get();

echo "Original timezone: " . $originalTz . "\n\n";

foreach ($testTimezones as $tz) {
    date_default_timezone_set($tz);
    $tzObj = new \DateTimeZone($tz);

    echo "┌──────────────────────────────────────────────────────────────────────────────┐\n";
    echo "│ Timezone: " . str_pad($tz, 66) . "\n";
    echo "└──────────────────────────────────────────────────────────────────────────────┘\n";

    // Test: Mock created in THIS timezone, output in THIS timezone
    // "2022-05-01 12:00:00" means noon in each respective timezone
    $slopeTz = null;
    ClockMock::executeAtFrozenDateTime(new \DateTime($mockDate, $tzObj), function () use (&$slopeTz) {
        $slopeTz = date("c");
    });

    Clock::set(new MockClock(new \DateTimeImmutable($mockDate, $tzObj)));
    $symfonyTz = date("c", Clock::get()->now()->getTimestamp());
    Clock::set(new NativeClock());

    echo "  Mock = \"$mockDate\" in $tz\n";
    echo "  slope-it date(\"c\"):    " . $slopeTz . "\n";
    echo "  symfony date(\"c\",...): " . $symfonyTz . "\n";
    echo "  ✅ MATCH: " . ($slopeTz === $symfonyTz ? "YES" : "NO") . "\n\n";
}

// Restore original timezone
date_default_timezone_set($originalTz);
echo "Restored timezone: " . date_default_timezone_get() . "\n\n";

// ============================================================================
// SUMMARY
// ============================================================================
echo "╔══════════════════════════════════════════════════════════════════════════════╗\n";
echo "║  SUMMARY                                                                     ║\n";
echo "╚══════════════════════════════════════════════════════════════════════════════╝\n\n";

echo "PART A - Normal operation:\n";
echo "  ✅ All three code changes produce EQUIVALENT results in normal operation\n\n";

echo "PART B - Mocked time:\n";
echo "  ✅ Both libraries produce IDENTICAL mocked timestamps\n\n";

echo "PART C - Timezone tests:\n";
echo "  ✅ Behavior is consistent across UTC, America/New_York, Europe/Amsterdam, Asia/Tokyo\n\n";

echo "Code changes made:\n";
echo "  time()                           →  Clock::get()->now()->getTimestamp()\n";
echo "  new DateTime(\"now\", UTC)         →  DateTime::createFromInterface(Clock::get()->now())->setTimezone(UTC)\n";
echo "  date(\"c\")                        →  date(\"c\", Clock::get()->now()->getTimestamp())\n\n";

echo "Why we switched:\n";
echo "  • uopz extension doesn't support PHP 8.5 (and had issues with 8.4)\n";
echo "  • symfony/clock works on all PHP versions without extensions\n";
echo "  • Same behavior, just explicit rather than implicit\n";

Running it locally gave me the following output:

Output
╔══════════════════════════════════════════════════════════════════════════════╗
║  SIDE-BY-SIDE: slope-it/clock-mock vs symfony/clock                          ║
║  PHP 8.2.30 with uopz extension                                         ║
╚══════════════════════════════════════════════════════════════════════════════╝

╔══════════════════════════════════════════════════════════════════════════════╗
║  PART A: NORMAL OPERATION - Old vs New code (no mocking)                     ║
╚══════════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────────┐
│ Change #1: $ts = time();                                                    │
│         → $ts = Clock::get()->now()->getTimestamp();                        │
└──────────────────────────────────────────────────────────────────────────────┘
  OLD: time()                              = 1767886074 (2026-01-08T15:27:54+00:00)
  NEW: Clock::get()->now()->getTimestamp() = 1767886074 (2026-01-08T15:27:54+00:00)
  ✅ EQUIVALENT: YES

┌──────────────────────────────────────────────────────────────────────────────┐
│ Change #2: $parsedDt = new \DateTime("now", new \DateTimeZone("UTC"));      │
│         → $parsedDt = \DateTime::createFromInterface(Clock::get()->now())   │
│                        ->setTimezone(new \DateTimeZone("UTC"));              │
└──────────────────────────────────────────────────────────────────────────────┘
  OLD: new DateTime("now", UTC)                        = 2026-01-08 15:27:54 UTC
  NEW: DateTime::createFromInterface(Clock::...)->UTC  = 2026-01-08 15:27:54 UTC
  ✅ EQUIVALENT: YES

┌──────────────────────────────────────────────────────────────────────────────┐
│ Change #3: return date("c");                                                │
│         → return date("c", Clock::get()->now()->getTimestamp());            │
└──────────────────────────────────────────────────────────────────────────────┘
  OLD: date("c")                                       = 2026-01-08T15:27:54+00:00
  NEW: date("c", Clock::get()->now()->getTimestamp())  = 2026-01-08T15:27:54+00:00
  ✅ EQUIVALENT: YES

┌──────────────────────────────────────────────────────────────────────────────┐
│ VERIFICATION: Prove uopz is NOT intercepting in Part A                      │
└──────────────────────────────────────────────────────────────────────────────┘
  System time (shell 'date +%s'): 1767886074 (2026-01-08 15:27:54)
  PHP time():                     1767886074 (2026-01-08 15:27:54)
  Clock::get()->now():            1767886074 (2026-01-08 15:27:54)
  ✅ All three match real time: YES (uopz not interfering)

╔══════════════════════════════════════════════════════════════════════════════╗
║  PART B: MOCKED TIME - slope-it vs symfony (mocking 2022-05-01 12:00:00)     ║
╚══════════════════════════════════════════════════════════════════════════════╝

┌──────────────────────────────────────────────────────────────────────────────┐
│ TEST 1: time() / Clock::get()->now()->getTimestamp()                        │
└──────────────────────────────────────────────────────────────────────────────┘
slope-it/clock-mock (uses uopz to intercept native time()):
  time() inside ClockMock = 1651406400 (2022-05-01T12:00:00+00:00)

symfony/clock (requires explicit Clock::get()->now()):
  Clock::get()->now()->getTimestamp() = 1651406400 (2022-05-01T12:00:00+00:00)

  ✅ MATCH: YES

┌──────────────────────────────────────────────────────────────────────────────┐
│ TEST 2: new DateTime("now") / DateTime::createFromInterface(Clock::...)     │
└──────────────────────────────────────────────────────────────────────────────┘
slope-it/clock-mock:
  new DateTime("now", UTC)->format("c") = 2022-05-01T12:00:00+00:00

symfony/clock:
  DateTime::createFromInterface(...)->format("c") = 2022-05-01T12:00:00+00:00

  ✅ MATCH: YES

┌──────────────────────────────────────────────────────────────────────────────┐
│ TEST 3: date("c") / date("c", Clock::get()->now()->getTimestamp())          │
└──────────────────────────────────────────────────────────────────────────────┘
slope-it/clock-mock:
  date("c") = 2022-05-01T12:00:00+00:00

symfony/clock:
  date("c", Clock::get()->now()->getTimestamp()) = 2022-05-01T12:00:00+00:00

  ✅ MATCH: YES

╔══════════════════════════════════════════════════════════════════════════════╗
║  PART C: TIMEZONE TESTS - Verify consistent behavior across timezones       ║
╚══════════════════════════════════════════════════════════════════════════════╝

Original timezone: UTC

┌──────────────────────────────────────────────────────────────────────────────┐
│ Timezone: UTC                                                               │
└──────────────────────────────────────────────────────────────────────────────┘
  Mock = "2022-05-01 12:00:00" in UTC
  slope-it date("c"):    2022-05-01T12:00:00+00:00
  symfony date("c",...): 2022-05-01T12:00:00+00:00
  ✅ MATCH: YES

┌──────────────────────────────────────────────────────────────────────────────┐
│ Timezone: America/New_York                                                  │
└──────────────────────────────────────────────────────────────────────────────┘
  Mock = "2022-05-01 12:00:00" in America/New_York
  slope-it date("c"):    2022-05-01T12:00:00-04:00
  symfony date("c",...): 2022-05-01T12:00:00-04:00
  ✅ MATCH: YES

┌──────────────────────────────────────────────────────────────────────────────┐
│ Timezone: Europe/Amsterdam                                                  │
└──────────────────────────────────────────────────────────────────────────────┘
  Mock = "2022-05-01 12:00:00" in Europe/Amsterdam
  slope-it date("c"):    2022-05-01T12:00:00+02:00
  symfony date("c",...): 2022-05-01T12:00:00+02:00
  ✅ MATCH: YES

┌──────────────────────────────────────────────────────────────────────────────┐
│ Timezone: Asia/Tokyo                                                        │
└──────────────────────────────────────────────────────────────────────────────┘
  Mock = "2022-05-01 12:00:00" in Asia/Tokyo
  slope-it date("c"):    2022-05-01T12:00:00+09:00
  symfony date("c",...): 2022-05-01T12:00:00+09:00
  ✅ MATCH: YES

Restored timezone: UTC

╔══════════════════════════════════════════════════════════════════════════════╗
║  SUMMARY                                                                     ║
╚══════════════════════════════════════════════════════════════════════════════╝

PART A - Normal operation:
  ✅ All three code changes produce EQUIVALENT results in normal operation

PART B - Mocked time:
  ✅ Both libraries produce IDENTICAL mocked timestamps

PART C - Timezone tests:
  ✅ Behavior is consistent across UTC, America/New_York, Europe/Amsterdam, Asia/Tokyo

Code changes made:
  time()                           →  Clock::get()->now()->getTimestamp()
  new DateTime("now", UTC)         →  DateTime::createFromInterface(Clock::get()->now())->setTimezone(UTC)
  date("c")                        →  date("c", Clock::get()->now()->getTimestamp())

Why we switched:
  • uopz extension doesn't support PHP 8.5 (and had issues with 8.4)
  • symfony/clock works on all PHP versions without extensions
  • Same behavior, just explicit rather than implicit

Giving me confidence that this is a safe "drop in replacement" change.

@vdekrijger vdekrijger changed the title chore(version upgrade): Bump to PHP8.5 feat(version upgrade)!: Bump to PHP8.5 Jan 8, 2026
$responseCode = $httpResponse->getResponseCode();

//close connection
curl_close($ch);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

public function decideResponseCases(): array
public static function decideResponseCases(): array
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enforced by PHPUnit 11

use PostHog\Consumer\ForkCurl;
use PostHog\Consumer\LibCurl;
use PostHog\Consumer\Socket;
use Symfony\Component\Clock\Clock;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

total PHP noob
(well, i ran a drupal site like 18 years ago)

is it necessary to go from standard library time to Symfony?

is there something that could trip us up here?
since the time of an event is so critical just worried about a silly mistake in the change (genuinely asking from a place of ignorance though :)))

Copy link
Contributor Author

@vdekrijger vdekrijger Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a sneaky one, the https://github.com/slope-it/clock-mock#mocked-functionsmethods library modifies the standard library and replaced the time() functionality with some custom logic. E.g. it intercepts the function at C level in order to be a "drop-in replacement" (context).

We needed to switch this to the Symfony clock in order to be able to manipulate it during our testing (same with the case in FeatureFlag.php), however I'm doing some testing to confirm that the behaviour is the same, as it's been a while since I've played with time based things in PHP 😄

Comment on lines +9 to +12
/**
* Trait providing time mocking functionality for tests using Symfony Clock.
* This replaces the slope-it/clock-mock dependency which required the uopz extension.
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ahhhhhh

ok, so the switch to symfony is for this mocking?

in which case, alongside the "is it correct/safe" question, is this (and i hate this word) idiomatic for PHP

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah traits are quite a common pattern within PHP, and they are frequently used to house "common / reusable methods" that span across a hierarchical / compositional object structure. + The added benefit here is that we cna have it act as a drop in replacement without changing much in the underlying function calls 😄 .

Here is a blog post of how Laravel does it (from 2023), which is one of the most popular PHP frameworks: https://laraveldaily.com/post/traits-laravel-eloquent-examples (or rather how spatie does it, but they are a very big contributor to Laravel).

backupStaticAttributes="false"
backupStaticProperties="false"
colors="true"
convertErrorsToExceptions="true"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explanation of the chnages:

  ┌─────────────────────────────┬────────────────────────┬────────────────────────┬─────────────────────────────────┐
  │           Change            │    Old (PHPUnit 9)     │    New (PHPUnit 11)    │             Reason              │
  ├─────────────────────────────┼────────────────────────┼────────────────────────┼─────────────────────────────────┤
  │ Schema                      │ phpunit.xsd 9.3        │ phpunit.xsd 11.5       │ Updated to match PHPUnit 11     │
  ├─────────────────────────────┼────────────────────────┼────────────────────────┼─────────────────────────────────┤
  │ backupStaticAttributes      │ backupStaticAttributes │ backupStaticProperties │ Renamed in PHPUnit 10           │
  ├─────────────────────────────┼────────────────────────┼────────────────────────┼─────────────────────────────────┤
  │ convertErrorsToExceptions   │ true                   │ removed                │ Now always true, option removed │
  ├─────────────────────────────┼────────────────────────┼────────────────────────┼─────────────────────────────────┤
  │ convertNoticesToExceptions  │ true                   │ removed                │ Now always true, option removed │
  ├─────────────────────────────┼────────────────────────┼────────────────────────┼─────────────────────────────────┤
  │ convertWarningsToExceptions │ true                   │ removed                │ Now always true, option removed │
  ├─────────────────────────────┼────────────────────────┼────────────────────────┼─────────────────────────────────┤
  │ forceCoversAnnotation       │ false                  │ removed                │ Removed in PHPUnit 10           │
  ├─────────────────────────────┼────────────────────────┼────────────────────────┼─────────────────────────────────┤
  │ timeoutForSmallTests        │ 1                      │ removed                │ Removed in PHPUnit 10           │
  ├─────────────────────────────┼────────────────────────┼────────────────────────┼─────────────────────────────────┤
  │ timeoutForMediumTests       │ 10                     │ removed                │ Removed in PHPUnit 10           │
  ├─────────────────────────────┼────────────────────────┼────────────────────────┼─────────────────────────────────┤
  │ timeoutForLargeTests        │ 60                     │ removed                │ Removed in PHPUnit 10           │
  ├─────────────────────────────┼────────────────────────┼────────────────────────┼─────────────────────────────────┤
  │ verbose                     │ false                  │ removed                │ Now a CLI-only option           │
  ├─────────────────────────────┼────────────────────────┼────────────────────────┼─────────────────────────────────┤
  │ cacheDirectory              │ none                   │ .phpunit.cache         │ Required in PHPUnit 10+         │
  ├─────────────────────────────┼────────────────────────┼────────────────────────┼─────────────────────────────────┤
  │ processUncoveredFiles       │ processUncoveredFiles  │ includeUncoveredFiles  │ Renamed                         │
  ├─────────────────────────────┼────────────────────────┼────────────────────────┼─────────────────────────────────┤
  │ <include> location          │ Inside <coverage>      │ Inside new <source>    │ Moved in PHPUnit 10             │
  ├─────────────────────────────┼────────────────────────┼────────────────────────┼─────────────────────────────────┤
  │ <logging/>                  │ Empty tag              │ removed                │ Was unused, removed             │
  └─────────────────────────────┴────────────────────────┴────────────────────────┴─────────────────────────────────┘
  Summary: PHPUnit 10 simplified the config by removing options that are now always-on or CLI-only, and reorganized where source directories are specified (moved from <coverage> to <source>). The changes are all structural to match PHPUnit 11's schema - no behavioral changes to how tests run.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so we need to bump to version 4 in here or it's automated?

Copy link
Contributor Author

@vdekrijger vdekrijger Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should work out of the box once I follow the steps listed here: https://github.com/PostHog/posthog-php/blob/master/RELEASING.md!

There is nothing happening automatically, but I will obviously tag it with v4 😄 !

Copy link
Member

@pauldambra pauldambra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should update the README to callout change in support
and i have no idea how to version and release but we'll need to do that as v4

(assuming that's happening then :approved.stamp:

Copy link
Member

@rafaeelaudibert rafaeelaudibert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure you follow through and do a major bump here. Since you're already deep into this, it'd be ideal if you could upgrade posthog-php to follow the new approval workflow outlined in https://posthog.com/handbook/engineering/sdks/releases. This will make releasing this library much safer/straight-forward.

@vdekrijger vdekrijger merged commit 9f522a9 into master Jan 9, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants