name: Test suite
on:
  workflow_call:
    inputs:
      all_variations:
        required: true
        type: boolean
      branch:
        required: true
        type: string
permissions:
  contents: read
jobs:
  LINUX_PPC64:
    if: ${{ fromJson(inputs.branch).jobs.LINUX_PPC64 }}
    name: LINUX_PPC64_ASAN_DEBUG_ZTS
    # This runs on a self-hosted runner; see https://wiki.php.net/systems/ci
    runs-on: [self-hosted, gentoo, ppc64]
    steps:
      - name: git checkout
        uses: actions/checkout@v6
        with:
          ref: ${{ fromJson(inputs.branch).ref }}
      - name: System info
        run: |
          echo "::group::Show host CPU info"
          lscpu
          echo "::endgroup::"
          echo "::group::Show installed packages"
          cat /var/lib/portage/world
          echo "::endgroup::"
      - name: ./configure
        uses: ./.github/actions/configure-gentoo
        with:
          configurationParameters: >-
            CFLAGS="-fsanitize=undefined,address -fno-sanitize=function -DZEND_TRACK_ARENA_ALLOC"
            LDFLAGS="-fsanitize=undefined,address -fno-sanitize=function"
            CC=clang-17
            CXX=clang++-17
            --enable-debug
            --enable-zts
          skipSlow: false # FIXME: This should likely include slow extensions
      - name: make
        run: make -j$(/usr/bin/nproc) >/dev/null
      # Skip an install action for now
      - name: Tests
        uses: ./.github/actions/test-gentoo
        # There is no PPC JIT, so rip this out
        with:
          runTestsParameters: >-
            --asan -x
      - name: Extra tests
        uses: ./.github/actions/extra-tests
  ALPINE:
    if: ${{ fromJson(inputs.branch).jobs.ALPINE }}
    name: ALPINE_X64_ASAN_DEBUG_ZTS
    runs-on: ubuntu-24.04
    container:
      image: 'alpine:3.22'
    steps:
      - name: git checkout
        uses: actions/checkout@v6
        with:
          ref: ${{ fromJson(inputs.branch).ref }}
      - name: apk
        uses: ./.github/actions/apk
      - name: System info
        run: |
          echo "::group::Show host CPU info"
          lscpu
          echo "::endgroup::"
          echo "::group::Show installed package versions"
          apk list
          echo "::endgroup::"
      - name: ccache
        uses: ./.github/actions/ccache
        with:
          name: "${{ github.job }}"
          cc: clang-20
          cxx: clang++-20
      - name: ./configure
        uses: ./.github/actions/configure-alpine
        with:
          configurationParameters: >-
            CFLAGS="-fsanitize=undefined,address -fno-sanitize=function -DZEND_TRACK_ARENA_ALLOC"
            LDFLAGS="-fsanitize=undefined,address -fno-sanitize=function"
            --enable-debug
            --enable-zts
          skipSlow: true # FIXME: This should likely include slow extensions
      - name: make
        run: make -j$(/usr/bin/nproc) >/dev/null
      - name: make install
        uses: ./.github/actions/install-alpine
      - name: Test Tracing JIT
        uses: ./.github/actions/test-alpine
        with:
          enableOpcache: true
          jitType: tracing
          runTestsParameters: >-
            --asan -x
      - name: Extra tests
        uses: ./.github/actions/extra-tests
  LINUX_X64:
    if: ${{ fromJson(inputs.branch).jobs.LINUX_X64 }}
    services:
      mysql:
        image: mysql:8.4
        ports:
          - 3306:3306
        env:
          MYSQL_DATABASE: test
          MYSQL_ROOT_PASSWORD: root
      postgres:
        image: postgres
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: test
      firebird:
        image: jacobalberty/firebird
        ports:
          - 3050:3050
        env:
          ISC_PASSWORD: test
          FIREBIRD_DATABASE: test.fdb
          FIREBIRD_USER: test
          FIREBIRD_PASSWORD: test
    strategy:
      fail-fast: false
      matrix: ${{ fromJson(inputs.branch).jobs.LINUX_X64.matrix }}
    name: "LINUX_X64${{ matrix.name }}_${{ matrix.debug && 'DEBUG' || 'RELEASE' }}_${{ matrix.zts && 'ZTS' || 'NTS' }}"
    runs-on: ubuntu-${{ fromJson(inputs.branch).config.ubuntu_version }}
    steps:
      - name: git checkout
        uses: actions/checkout@v6
        with:
          ref: ${{ fromJson(inputs.branch).ref }}
      - name: Create MSSQL container
        uses: ./.github/actions/setup-mssql
      - name: apt
        uses: ./.github/actions/apt-x64
        with:
          asan: ${{ matrix.asan && 'true' || 'false' }}
      - name: System info
        run: |
          echo "::group::Show host CPU info"
          lscpu
          echo "::endgroup::"
          echo "::group::Show installed package versions"
          dpkg -l
          echo "::endgroup::"
      - name: Setup Caddy server
        uses: ./.github/actions/setup-caddy
      - name: ccache
        uses: ./.github/actions/ccache
        with:
          # This duplicates the "job.name" expression above because
          # GitHub has no way to query the job name (github.job is the
          # job id, not the job name)
          name: "LINUX_X64${{ matrix.name }}_${{ matrix.debug && 'DEBUG' || 'RELEASE' }}_${{ matrix.zts && 'ZTS' || 'NTS' }}"
      - name: ./configure
        uses: ./.github/actions/configure-x64
        with:
          configurationParameters: >-
            ${{ matrix.asan && 'CFLAGS="-fsanitize=undefined,address -DZEND_TRACK_ARENA_ALLOC" LDFLAGS="-fsanitize=undefined,address"' || '' }}
            ${{ matrix.variation && 'CFLAGS="-DZEND_RC_DEBUG=1 -DPROFITABILITY_CHECKS=0 -DZEND_VERIFY_FUNC_INFO=1 -DZEND_VERIFY_TYPE_INFERENCE"' || '' }}
            ${{ (matrix.variation && fromJson(inputs.branch).jobs.LINUX_X64.config.variation_enable_zend_max_execution_timers) && '--enable-zend-max-execution-timers' || '' }}
            --${{ matrix.debug && 'enable' || 'disable' }}-debug
            ${{ matrix.debug && 'CXXFLAGS="-D_GLIBCXX_ASSERTIONS"' || '' }}
            --${{ matrix.zts && 'enable' || 'disable' }}-zts
          asan: ${{ matrix.asan && 'true' || 'false' }}
          skipSlow: ${{ (matrix.asan && !inputs.all_variations) && 'true' || 'false' }}
      - name: make
        run: make -j$(/usr/bin/nproc) >/dev/null
      - name: make install
        uses: ./.github/actions/install-linux
      - name: Setup
        if: ${{ !matrix.asan || inputs.all_variations }}
        uses: ./.github/actions/setup-x64
      - name: Test
        if: ${{ inputs.all_variations || !matrix.asan }}
        uses: ./.github/actions/test-linux
        with:
          runTestsParameters: >-
            ${{ matrix.asan && '--asan' || '' }}
            ${{ matrix.repeat && '--repeat 2' || '' }}
            ${{ matrix.variation && '-d zend_test.observer.enabled=1 -d zend_test.observer.show_output=0' || '' }}
          idleCpu: ${{ matrix.asan && 'true' || 'false' }}
      - name: Test Tracing JIT
        if: ${{ inputs.all_variations || matrix.asan }}
        uses: ./.github/actions/test-linux
        with:
          enableOpcache: true
          jitType: tracing
          runTestsParameters: >-
            ${{ matrix.asan && '--asan' || '' }}
            ${{ (matrix.asan && !inputs.all_variations) && '-x' || '' }}
            ${{ matrix.repeat && '--repeat 2' || '' }}
            ${{ matrix.variation && '-d zend_test.observer.enabled=1 -d zend_test.observer.show_output=0' || '' }}
      - name: Test OpCache
        if: ${{ inputs.all_variations }}
        uses: ./.github/actions/test-linux
        with:
          enableOpcache: true
          runTestsParameters: >-
            ${{ matrix.asan && '--asan' || '' }}
            ${{ matrix.repeat && '--repeat 2' || '' }}
            ${{ matrix.variation && '-d zend_test.observer.enabled=1 -d zend_test.observer.show_output=0' || '' }}
      - name: Test Function JIT
        # ASAN frequently timeouts. Each test run takes ~90 minutes, we can
        # avoid running into the 6 hour timeout by skipping the function JIT.
        if: ${{ !matrix.asan }}
        uses: ./.github/actions/test-linux
        with:
          enableOpcache: true
          jitType: function
          runTestsParameters: >-
            ${{ matrix.repeat && '--repeat 2' || '' }}
            ${{ matrix.variation && '-d zend_test.observer.enabled=1 -d zend_test.observer.show_output=0' || '' }}
      - name: Extra tests
        uses: ./.github/actions/extra-tests
      - name: Verify generated files are up to date
        uses: ./.github/actions/verify-generated-files
  LINUX_X32:
    if: ${{ fromJson(inputs.branch).jobs.LINUX_X32 }}
    strategy:
      fail-fast: false
      matrix: ${{ fromJson(inputs.branch).jobs.LINUX_X32.matrix }}
    name: "LINUX_X32_${{ matrix.debug && 'DEBUG' || 'RELEASE' }}_${{ matrix.zts && 'ZTS' || 'NTS' }}"
    runs-on: ubuntu-latest
    container:
      image: ubuntu:${{ fromJson(inputs.branch).config.ubuntu_version }}
    env:
      MYSQL_TEST_HOST: mysql
      PDO_MYSQL_TEST_DSN: mysql:host=mysql;dbname=test
      PDO_MYSQL_TEST_HOST: mysql
      PDO_FIREBIRD_TEST_DSN: firebird:dbname=firebird:test.fdb
    services:
      mysql:
        image: mysql:8.4
        ports:
          - 3306:3306
        env:
          MYSQL_DATABASE: test
          MYSQL_ROOT_PASSWORD: root
      firebird:
        image: jacobalberty/firebird
        ports:
          - 3050:3050
        env:
          ISC_PASSWORD: test
          FIREBIRD_DATABASE: test.fdb
          FIREBIRD_USER: test
          FIREBIRD_PASSWORD: test
    steps:
      - name: git checkout
        uses: actions/checkout@v6
        with:
          ref: ${{ fromJson(inputs.branch).ref }}
      - name: apt
        uses: ./.github/actions/apt-x32
      - name: ccache
        uses: ./.github/actions/ccache
        with:
          name: "LINUX_X32_${{ matrix.debug && 'DEBUG' || 'RELEASE' }}_${{ matrix.zts && 'ZTS' || 'NTS' }}"
      - name: System info
        run: |
          echo "::group::Show host CPU info"
          lscpu
          echo "::endgroup::"
          echo "::group::Show installed package versions"
          dpkg -l
          echo "::endgroup::"
      - name: ./configure
        uses: ./.github/actions/configure-x32
        with:
          configurationParameters: >-
            --${{ matrix.debug && 'enable' || 'disable' }}-debug
            --${{ matrix.zts && 'enable' || 'disable' }}-zts
      - name: make
        run: make -j$(/usr/bin/nproc) >/dev/null
      - name: make install
        uses: ./.github/actions/install-linux-x32
      - name: Test
        if: ${{ inputs.all_variations }}
        uses: ./.github/actions/test-linux
      - name: Test Tracing JIT
        uses: ./.github/actions/test-linux
        with:
          enableOpcache: true
          jitType: tracing
      - name: Test OpCache
        if: ${{ inputs.all_variations }}
        uses: ./.github/actions/test-linux
        with:
          enableOpcache: true
      - name: Test Function JIT
        if: ${{ inputs.all_variations }}
        uses: ./.github/actions/test-linux
        with:
          enableOpcache: true
          jitType: function
      - name: Extra tests
        uses: ./.github/actions/extra-tests
  MACOS:
    if: ${{ fromJson(inputs.branch).jobs.MACOS }}
    strategy:
      fail-fast: false
      matrix: ${{ fromJson(inputs.branch).jobs.MACOS.matrix }}
    name: "MACOS_${{ matrix.arch }}_${{ matrix.debug && 'DEBUG' || 'RELEASE' }}_${{ matrix.zts && 'ZTS' || 'NTS' }}"
    runs-on: macos-${{ matrix.arch == 'X64' && '15-intel' || fromJson(inputs.branch).jobs.MACOS.config.arm64_version }}
    steps:
      - name: git checkout
        uses: actions/checkout@v6
        with:
          ref: ${{ fromJson(inputs.branch).ref }}
      - name: Update clang
        timeout-minutes: 10
        uses: ./.github/actions/macos-update-clang
      - name: brew
        timeout-minutes: 10
        uses: ./.github/actions/brew
      - name: ccache
        uses: ./.github/actions/ccache
        with:
          name: "MACOS_${{ matrix.arch }}_${{ matrix.debug && 'DEBUG' || 'RELEASE' }}_${{ matrix.zts && 'ZTS' || 'NTS' }}"
      - name: ./configure
        uses: ./.github/actions/configure-macos
        with:
          configurationParameters: >-
            --${{ matrix.debug && 'enable' || 'disable' }}-debug
            --${{ matrix.zts && 'enable' || 'disable' }}-zts
      - name: make
        run: |-
          export PATH="$(brew --prefix)/opt/bison/bin:$PATH"
          make -j$(sysctl -n hw.logicalcpu) >/dev/null
      - name: make install
        run: sudo make install
      - name: Test
        if: ${{ inputs.all_variations }}
        uses: ./.github/actions/test-macos
      - name: Test Tracing JIT
        if: ${{ matrix.arch == 'X64' || !matrix.zts }}
        uses: ./.github/actions/test-macos
        with:
          enableOpcache: true
          jitType: tracing
      - name: Test OpCache
        if: ${{ inputs.all_variations || (matrix.arch == 'ARM64' && matrix.zts) }}
        uses: ./.github/actions/test-macos
        with:
          enableOpcache: true
      - name: Test Function JIT
        if: ${{ inputs.all_variations && (matrix.arch == 'X64' || !matrix.zts) }}
        uses: ./.github/actions/test-macos
        with:
          enableOpcache: true
          jitType: function
      - name: Extra tests
        uses: ./.github/actions/extra-tests
      - name: Verify generated files are up to date
        uses: ./.github/actions/verify-generated-files
  COVERAGE:
    if: ${{ fromJson(inputs.branch).jobs.COVERAGE }}
    services:
      mysql:
        image: mysql:8.4
        ports:
          - 3306:3306
        env:
          MYSQL_DATABASE: test
          MYSQL_ROOT_PASSWORD: root
      postgres:
        image: postgres
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: test
      firebird:
        image: jacobalberty/firebird
        ports:
          - 3050:3050
        env:
          ISC_PASSWORD: test
          FIREBIRD_DATABASE: test.fdb
          FIREBIRD_USER: test
          FIREBIRD_PASSWORD: test
    runs-on: ubuntu-24.04
    steps:
      - name: git checkout
        uses: actions/checkout@v6
        with:
          ref: ${{ fromJson(inputs.branch).ref }}
      - name: Create MSSQL container
        uses: ./.github/actions/setup-mssql
      - name: apt
        uses: ./.github/actions/apt-x64
      - name: Install gcovr
        run: sudo -H pip install gcovr
      - name: ./configure
        uses: ./.github/actions/configure-x64
        with:
          configurationParameters: --enable-debug --disable-zts --enable-gcov
      - name: make
        run: make -j$(/usr/bin/nproc) >/dev/null
      - name: make install
        uses: ./.github/actions/install-linux
      - name: Setup
        uses: ./.github/actions/setup-x64
      # We only test with OpCache, the difference in coverage is negligible
      - name: Test OpCache
        uses: ./.github/actions/test-linux
        with:
          enableOpcache: true
          jitType: tracing
      - name: Generate coverage report
        if: ${{ !cancelled() }}
        run: make gcovr-xml
      - uses: codecov/codecov-action@v6
        if: ${{ !cancelled() }}
        with:
          disable_search: true
          fail_ci_if_error: true
          files: gcovr.xml
          token: ${{ secrets.CODECOV_TOKEN }}
          verbose: true
  COMMUNITY:
    if: ${{ fromJson(inputs.branch).jobs.COMMUNITY }}
    strategy:
      fail-fast: false
      matrix: ${{ fromJson(inputs.branch).jobs.COMMUNITY.matrix }}
    name: "COMMUNITY_${{ matrix.type }}"
    runs-on: ubuntu-${{ fromJson(inputs.branch).config.ubuntu_version }}
    env:
      ASAN_OPTIONS: exitcode=139
      UBSAN_OPTIONS: print_stacktrace=1
      USE_ZEND_ALLOC: 0
      USE_TRACKED_ALLOC: 1
    steps:
      - name: git checkout
        uses: actions/checkout@v6
        with:
          ref: ${{ fromJson(inputs.branch).ref }}
      - name: apt
        uses: ./.github/actions/apt-x64
      - name: ccache
        uses: ./.github/actions/ccache
        with:
          name: "COMMUNITY_${{ matrix.type }}"
      - name: ./configure
        uses: ./.github/actions/configure-x64
        with:
          # CFLAGS removes O2, so we have to add it again...
          configurationParameters: >-
            --enable-zts
            ${{ matrix.type == 'asan' && '--enable-debug CFLAGS="-fsanitize=undefined,address -fno-sanitize-recover -DZEND_TRACK_ARENA_ALLOC" LDFLAGS="-fsanitize=undefined,address"' || '' }}
            ${{ matrix.type == 'verify_type_inference' && 'CFLAGS="-DZEND_VERIFY_TYPE_INFERENCE -O2"' || '' }}
      - name: make
        run: make -j$(/usr/bin/nproc) >/dev/null
      - name: make install
        uses: ./.github/actions/install-linux
      - name: Setup
        run: |
          sudo service mysql start
          mysql -uroot -proot -e "CREATE DATABASE IF NOT EXISTS test"
          mysql -uroot -proot -e "SET GLOBAL local_infile = true"
      - name: Enable Opcache
        run: |
          echo memory_limit=-1 >> /etc/php.d/opcache.ini
          echo opcache.enable_cli=1 >> /etc/php.d/opcache.ini
          echo opcache.enable=1 >> /etc/php.d/opcache.ini
          echo opcache.protect_memory=1 >> /etc/php.d/opcache.ini
          echo opcache.memory_consumption=256M >> /etc/php.d/opcache.ini
          echo opcache.file_update_protection=0 >> /etc/php.d/opcache.ini
          echo opcache.interned_strings_buffer=64 >> /etc/php.d/opcache.ini
          echo opcache.max_accelerated_files=100000 >> /etc/php.d/opcache.ini
      - name: Enable JIT
        if: ${{ matrix.type != 'verify_type_inference' }}
        run: |
          echo opcache.jit=tracing >> /etc/php.d/opcache.ini
          echo opcache.jit_buffer_size=1G >> /etc/php.d/opcache.ini
          echo opcache.jit_max_root_traces=100000 >> /etc/php.d/opcache.ini
          echo opcache.jit_max_side_traces=100000 >> /etc/php.d/opcache.ini
          echo opcache.jit_max_exit_counters=100000 >> /etc/php.d/opcache.ini
          echo opcache.jit_hot_loop=1 >> /etc/php.d/opcache.ini
          echo opcache.jit_hot_func=1 >> /etc/php.d/opcache.ini
          echo opcache.jit_hot_return=1 >> /etc/php.d/opcache.ini
          echo opcache.jit_hot_side_exit=1 >> /etc/php.d/opcache.ini
          php -v
      - name: Test AMPHP
        if: ${{ !cancelled() }}
        run: |
          repositories="amp cache dns file http parallel parser pipeline process serialization socket sync websocket-client websocket-server"
          X=0
          for repository in $repositories; do
            echo "::group::$repository"
            git clone "https://github.com/amphp/$repository.git" "amphp-$repository" --depth 1
            cd "amphp-$repository"
            git rev-parse HEAD
            php /usr/bin/composer install --no-progress --ignore-platform-req=php+
            EXIT_CODE=0
            vendor/bin/phpunit || EXIT_CODE=$?
            echo -e "\n::endgroup::"
            if [ ${EXIT_CODE:-0} -gt 128 ]; then
              X=1;
              echo "Failed"
            fi
            cd ..
          done
          exit $X
      - name: Test Laravel
        if: ${{ !cancelled() }}
        run: |
          branch=${{ fromJson(inputs.branch).jobs.COMMUNITY.config.laravel_version }}
          git clone https://github.com/laravel/framework.git --depth=1 ${branch:+--branch="$branch"}
          cd framework
          git rev-parse HEAD
          php /usr/bin/composer install --no-progress --ignore-platform-req=php+
          # Hack to disable a test that hangs
          php -r '$c = file_get_contents("tests/Filesystem/FilesystemTest.php"); $c = str_replace("public function testSharedGet()", "#[\\PHPUnit\\Framework\\Attributes\\Group('"'"'skip'"'"')]\n    public function testSharedGet()", $c); file_put_contents("tests/Filesystem/FilesystemTest.php", $c);'
          php vendor/bin/phpunit --exclude-group skip || EXIT_CODE=$?
          if [ ${EXIT_CODE:-0} -gt 128 ]; then
            exit 1
          fi
      - name: Test ReactPHP
        if: ${{ !cancelled() }}
        run: |
          repositories="async cache child-process datagram dns event-loop promise promise-stream promise-timer stream"
          X=0
          for repository in $repositories; do
            echo "::group::$repository"
            git clone "https://github.com/reactphp/$repository.git" "reactphp-$repository" --depth 1
            cd "reactphp-$repository"
            git rev-parse HEAD
            php /usr/bin/composer install --no-progress --ignore-platform-req=php+
            EXIT_CODE=0
            vendor/bin/phpunit || EXIT_CODE=$?
            echo -e "\n::endgroup::"
            if [ $[EXIT_CODE:-0} -gt 128 ]; then
              X=1;
              echo "Failed"
            fi
            cd ..
          done
          exit $X
      - name: Test Revolt PHP
        if: ${{ !cancelled() }}
        run: |
          git clone https://github.com/revoltphp/event-loop.git --depth=1
          cd event-loop
          git rev-parse HEAD
          php /usr/bin/composer install --no-progress --ignore-platform-req=php+
          vendor/bin/phpunit || EXIT_CODE=$?
          if [ ${EXIT_CODE:-0} -gt 128 ]; then
            exit 1
          fi
      - name: Test Symfony
        if: ${{ !cancelled() }}
        run: |
          branch=${{ fromJson(inputs.branch).jobs.COMMUNITY.config.symfony_version }}
          git clone https://github.com/symfony/symfony.git --depth=1 ${branch:+--branch="$branch"}
          cd symfony
          git rev-parse HEAD
          php /usr/bin/composer install --no-progress --ignore-platform-req=php+
          php ./phpunit install
          # Test causes a heap-buffer-overflow but I cannot reproduce it locally...
          php -r '$c = file_get_contents("src/Symfony/Component/HtmlSanitizer/Tests/HtmlSanitizerCustomTest.php"); $c = str_replace("public function testSanitizeDeepNestedString()", "#[\\PHPUnit\\Framework\\Attributes\\Group('"'"'skip'"'"')]\n    public function testSanitizeDeepNestedString()", $c); file_put_contents("src/Symfony/Component/HtmlSanitizer/Tests/HtmlSanitizerCustomTest.php", $c);'
          # Buggy FFI test in Symfony, see https://github.com/symfony/symfony/issues/47668
          php -r '$c = file_get_contents("src/Symfony/Component/VarDumper/Tests/Caster/FFICasterTest.php"); $c = str_replace("public function testCastNonTrailingCharPointer()", "#[\\PHPUnit\\Framework\\Attributes\\Group('"'"'skip'"'"')]\n    public function testCastNonTrailingCharPointer()", $c); file_put_contents("src/Symfony/Component/VarDumper/Tests/Caster/FFICasterTest.php", $c);'
          export SYMFONY_DEPRECATIONS_HELPER=max[total]=999
          X=0
          for component in $(find src/Symfony -mindepth 2 -type f -name phpunit.xml.dist -printf '%h\n'); do
            echo "::group::$component"
            EXIT_CODE=0
            php ./phpunit $component --exclude-group tty --exclude-group benchmark --exclude-group intl-data --exclude-group transient --exclude-group skip || EXIT_CODE=$?
            echo -e "\n::endgroup::"
            if [ ${EXIT_CODE:-0} -gt 128 ]; then
              X=1;
              echo "Failed"
            fi
          done
          exit $X
      - name: Test PHPUnit
        if: ${{ !cancelled() }}
        run: |
          git clone https://github.com/sebastianbergmann/phpunit.git --branch=main --depth=1
          cd phpunit
          git rev-parse HEAD
          php /usr/bin/composer install --no-progress --ignore-platform-req=php+
          php ./phpunit || EXIT_CODE=$?
          if [ ${EXIT_CODE:-0} -gt 128 ]; then
            exit 1
          fi
      - name: 'Symfony Preloading'
        # composer create-project will automatically pick the right Symfony version for us.
        if: ${{ !cancelled() }}
        run: |
          php /usr/bin/composer create-project symfony/symfony-demo symfony_demo --no-progress --ignore-platform-req=php+
          cd symfony_demo
          git rev-parse HEAD
          sed -i 's/PHP_SAPI/"cli-server"/g' var/cache/dev/App_KernelDevDebugContainer.preload.php
          php -d opcache.preload=var/cache/dev/App_KernelDevDebugContainer.preload.php public/index.php
      - name: Test Wordpress
        if: ${{ !cancelled() }}
        run: |
          git clone https://github.com/WordPress/wordpress-develop.git wordpress --depth=1
          cd wordpress
          git rev-parse HEAD
          php /usr/bin/composer install --no-progress --ignore-platform-req=php+
          cp wp-tests-config-sample.php wp-tests-config.php
          sed -i 's/youremptytestdbnamehere/test/g' wp-tests-config.php
          sed -i 's/yourusernamehere/root/g' wp-tests-config.php
          sed -i 's/yourpasswordhere/root/g' wp-tests-config.php
          php vendor/bin/phpunit || EXIT_CODE=$?
          if [ $EXIT_CODE -gt 128 ]; then
            exit 1
          fi
  OPCACHE_VARIATION:
    if: ${{ fromJson(inputs.branch).jobs.OPCACHE_VARIATION }}
    services:
      mysql:
        image: mysql:8.4
        ports:
          - 3306:3306
        env:
          MYSQL_DATABASE: test
          MYSQL_ROOT_PASSWORD: root
      postgres:
        image: postgres
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: test
      firebird:
        image: jacobalberty/firebird
        ports:
          - 3050:3050
        env:
          ISC_PASSWORD: test
          FIREBIRD_DATABASE: test.fdb
          FIREBIRD_USER: test
          FIREBIRD_PASSWORD: test
    name: OPCACHE_VARIATION
    runs-on: ubuntu-${{ fromJson(inputs.branch).config.ubuntu_version }}
    steps:
      - name: git checkout
        uses: actions/checkout@v6
        with:
          ref: ${{ fromJson(inputs.branch).ref }}
      - name: Create MSSQL container
        uses: ./.github/actions/setup-mssql
      - name: apt
        uses: ./.github/actions/apt-x64
      - name: ccache
        uses: ./.github/actions/ccache
        with:
          name: "${{ github.job }}"
      - name: ./configure
        uses: ./.github/actions/configure-x64
        with:
          configurationParameters: >-
            --enable-debug --disable-zts
      - name: make
        run: make -j$(/usr/bin/nproc) >/dev/null
      - name: make install
        uses: ./.github/actions/install-linux
      - name: Setup
        uses: ./.github/actions/setup-x64
      - name: Test File Cache (prime shm)
        uses: ./.github/actions/test-linux
        with:
          enableOpcache: true
          runTestsParameters: >-
            --file-cache-prime
      - name: Test File Cache (prime shm, use shm)
        uses: ./.github/actions/test-linux
        with:
          enableOpcache: true
          runTestsParameters: >-
            --file-cache-use
      - name: Test File Cache (prime shm, use file)
        uses: ./.github/actions/test-linux
        with:
          enableOpcache: true
          runTestsParameters: >-
            --file-cache-use
            -d opcache.file_cache_only=1
      - name: Test File Cache Only (prime)
        uses: ./.github/actions/test-linux
        with:
          enableOpcache: true
          runTestsParameters: >-
            --file-cache-prime
            -d opcache.file_cache_only=1
      - name: Test File Cache Only (use)
        uses: ./.github/actions/test-linux
        with:
          enableOpcache: true
          runTestsParameters: >-
            --file-cache-use
            -d opcache.file_cache_only=1
      - name: Verify generated files are up to date
        uses: ./.github/actions/verify-generated-files
  MSAN:
    if: ${{ fromJson(inputs.branch).jobs.MSAN }}
    name: MSAN
    runs-on: ubuntu-${{ fromJson(inputs.branch).config.ubuntu_version }}
    steps:
      - name: git checkout
        uses: actions/checkout@v6
        with:
          ref: ${{ fromJson(inputs.branch).ref }}
      - name: apt
        uses: ./.github/actions/apt-x64
      - name: ccache
        uses: ./.github/actions/ccache
        with:
          name: "${{ github.job }}"
          cc: clang
          cxx: clang++
      - name: ./configure
        run: |
          export CFLAGS="-DZEND_TRACK_ARENA_ALLOC"
          ./buildconf --force
          # msan requires all used libraries to be instrumented,
          # so we should avoiding linking against anything but libc here
          ./configure \
            --enable-debug \
            --enable-zts \
            --enable-option-checking=fatal \
            --prefix=/usr \
            --without-sqlite3 \
            --without-pdo-sqlite \
            --without-libxml \
            --disable-dom \
            --disable-simplexml \
            --disable-xml \
            --disable-xmlreader \
            --disable-xmlwriter \
            --without-pcre-jit \
            --disable-opcache-jit \
            --enable-phpdbg \
            --enable-fpm \
            --with-pdo-mysql=mysqlnd \
            --with-mysqli=mysqlnd \
            --disable-mysqlnd-compression-support \
            --without-pear \
            --enable-exif \
            --enable-sysvsem \
            --enable-sysvshm \
            --enable-shmop \
            --enable-pcntl \
            --enable-mbstring \
            --disable-mbregex \
            --enable-sockets \
            --enable-bcmath \
            --enable-calendar \
            --enable-ftp \
            --enable-zend-test \
            --enable-werror \
            --enable-memory-sanitizer \
            --with-config-file-path=/etc \
            --with-config-file-scan-dir=/etc/php.d \
            --enable-dl-test=shared
      - name: make
        run: make -j$(/usr/bin/nproc) >/dev/null
      - name: make install
        run: |
          sudo make install
          sudo mkdir -p /etc/php.d
          sudo chmod 777 /etc/php.d
          echo mysqli.default_socket=/var/run/mysqld/mysqld.sock    > /etc/php.d/mysqli.ini
          echo pdo_mysql.default_socket=/var/run/mysqld/mysqld.sock > /etc/php.d/pdo_mysql.ini
      - name: Setup
        run: |
          set -x
          sudo service mysql start
          mysql -uroot -proot -e "CREATE DATABASE IF NOT EXISTS test"
          # Ensure local_infile tests can run.
          mysql -uroot -proot -e "SET GLOBAL local_infile = true"
          sudo locale-gen de_DE
      - name: Test
        uses: ./.github/actions/test-linux
        with:
          runTestsParameters: >-
            --msan
      - name: Test Opcache
        uses: ./.github/actions/test-linux
        with:
          enableOpcache: true
          runTestsParameters: >-
            --msan
      - name: Verify generated files are up to date
        uses: ./.github/actions/verify-generated-files
  LIBMYSQLCLIENT:
    if: ${{ fromJson(inputs.branch).jobs.LIBMYSQLCLIENT }}
    name: LIBMYSQLCLIENT
    runs-on: ubuntu-${{ fromJson(inputs.branch).config.ubuntu_version }}
    steps:
      - name: git checkout
        uses: actions/checkout@v6
        with:
          ref: ${{ fromJson(inputs.branch).ref }}
      - name: apt
        run: |
          sudo apt-get update -y | true
          sudo apt install bison re2c
      - name: Setup
        run: |
          sudo service mysql start
          mysql -uroot -proot -e "CREATE DATABASE IF NOT EXISTS test"
          # Ensure local_infile tests can run.
          mysql -uroot -proot -e "SET GLOBAL local_infile = true"
      - name: Build mysql-8.0
        uses: ./.github/actions/build-libmysqlclient
        with:
          configurationParameters: '--enable-werror'
          libmysql: mysql-8.0.37-linux-glibc2.28-x86_64.tar.xz
      - name: Test mysql-8.0
        uses: ./.github/actions/test-libmysqlclient
      - name: Build mysql-8.4
        uses: ./.github/actions/build-libmysqlclient
        with:
          configurationParameters: '--enable-werror'
          libmysql: mysql-8.4.0-linux-glibc2.28-x86_64.tar.xz
      - name: Test mysql-8.4
        uses: ./.github/actions/test-libmysqlclient
      - name: Verify generated files are up to date
        uses: ./.github/actions/verify-generated-files
  PECL:
    if: ${{ fromJson(inputs.branch).jobs.PECL }}
    runs-on: ubuntu-24.04
    steps:
      - name: git checkout PHP
        uses: actions/checkout@v6
        with:
          path: php
          ref: ${{ fromJson(inputs.branch).ref }}
      # Used for ccache action
      - name: Move .github
        run: mv php/.github .
      - name: git checkout apcu
        uses: actions/checkout@v6
        with:
          repository: krakjoe/apcu
          path: apcu
      - name: git checkout imagick
        uses: actions/checkout@v6
        with:
          repository: Imagick/imagick
          path: imagick
      - name: git checkout memcached
        uses: actions/checkout@v6
        with:
          repository: php-memcached-dev/php-memcached
          path: memcached
      - name: git checkout redis
        if: ${{ false }}
        uses: actions/checkout@v6
        with:
          repository: phpredis/phpredis
          path: redis
      - name: git checkout xdebug
        uses: actions/checkout@v6
        with:
          repository: xdebug/xdebug
          path: xdebug
      - name: git checkout yaml
        uses: actions/checkout@v6
        with:
          repository: php/pecl-file_formats-yaml
          path: yaml
      - name: apt
        run: |
          sudo apt-get update
          sudo apt-get install -y --no-install-recommends \
            ccache \
            libmemcached-dev \
            imagemagick \
            libmagickwand-dev \
            bison \
            re2c
      - name: ccache
        uses: ./.github/actions/ccache
        with:
          name: "${{ github.job }}"
          php_directory: php
      - name: build PHP
        run: |
          cd php
          ./buildconf --force
          ./configure \
            --enable-option-checking=fatal \
            --prefix=/opt/php \
            --enable-cli \
            --disable-all \
            --enable-session \
            --enable-werror
          make -j$(/usr/bin/nproc)
          sudo make install
      - name: build apcu
        run: |
          cd apcu
          /opt/php/bin/phpize
          ./configure --prefix=/opt/php --with-php-config=/opt/php/bin/php-config
          make -j$(/usr/bin/nproc)
      - name: build imagick
        run: |
          cd imagick
          /opt/php/bin/phpize
          ./configure --prefix=/opt/php --with-php-config=/opt/php/bin/php-config
          make -j$(/usr/bin/nproc)
      - name: build memcached
        run: |
          cd memcached
          /opt/php/bin/phpize
          ./configure --prefix=/opt/php --with-php-config=/opt/php/bin/php-config
          make -j$(/usr/bin/nproc)
      - name: build redis
        if: ${{ false }}
        run: |
          cd redis
          /opt/php/bin/phpize
          ./configure --prefix=/opt/php --with-php-config=/opt/php/bin/php-config
          make -j$(/usr/bin/nproc)
      - name: build xdebug
        run: |
          cd xdebug
          /opt/php/bin/phpize
          ./configure --prefix=/opt/php --with-php-config=/opt/php/bin/php-config
          make -j$(/usr/bin/nproc)
      - name: build yaml
        run: |
          cd yaml
          /opt/php/bin/phpize
          ./configure --prefix=/opt/php --with-php-config=/opt/php/bin/php-config
          make -j$(/usr/bin/nproc)
  WINDOWS:
    if: ${{ fromJson(inputs.branch).jobs.WINDOWS }}
    strategy:
      fail-fast: false
      matrix: ${{ fromJson(inputs.branch).jobs.WINDOWS.matrix }}
    name: "WINDOWS_${{ matrix.x64 && 'X64' || 'X86' }}_${{ matrix.zts && 'ZTS' || 'NTS' }}${{ matrix.asan && '_ASAN' || ''}}${{ matrix.clang && '_CLANG' || ''}}"
    runs-on: windows-2022
    env:
      PHP_BUILD_CACHE_BASE_DIR: C:\build-cache
      PHP_BUILD_OBJ_DIR: C:\obj
      PHP_BUILD_CACHE_SDK_DIR: C:\build-cache\sdk
      PHP_BUILD_SDK_BRANCH: php-sdk-2.5.0
      PHP_BUILD_CRT: ${{ fromJson(inputs.branch).jobs.WINDOWS.config.vs_crt_version }}
      PLATFORM: ${{ matrix.x64 && 'x64' || 'x86' }}
      THREAD_SAFE: "${{ matrix.zts && '1' || '0' }}"
      INTRINSICS: "${{ matrix.zts && 'AVX2' || '' }}"
      PARALLEL: -j2
      OPCACHE: "${{ matrix.opcache && '1' || '0' }}"
      ASAN: "${{ matrix.asan && '1' || '0' }}"
      CLANG_TOOLSET: "${{ matrix.clang && '1' || '0' }}"
    steps:
      - name: git config
        run: git config --global core.autocrlf false && git config --global core.eol lf
      - name: git checkout
        uses: actions/checkout@v6
        with:
          ref: ${{ fromJson(inputs.branch).ref }}
      - name: Setup
        uses: ./.github/actions/setup-windows
      - name: Build
        run: .github/scripts/windows/build.bat
      - name: Test
        run: .github/scripts/windows/test.bat
  FREEBSD:
    if: ${{ fromJson(inputs.branch).jobs.FREEBSD }}
    strategy:
      fail-fast: false
      matrix: ${{ fromJson(inputs.branch).jobs.FREEBSD.matrix }}
    name: "FREEBSD_${{ matrix.zts && 'ZTS' || 'NTS' }}"
    runs-on: ubuntu-latest
    timeout-minutes: 50
    steps:
      - name: git checkout
        uses: actions/checkout@v6
        with:
          ref: ${{ fromJson(inputs.branch).ref }}
      - name: FreeBSD
        uses: ./.github/actions/freebsd
        with:
          configurationParameters: >-
            --${{ matrix.zts && 'enable' || 'disable' }}-zts
          runExtraTests: true
  SOLARIS:
    if: ${{ fromJson(inputs.branch).jobs.SOLARIS }}
    name: "SOLARIS"
    runs-on: ubuntu-latest
    timeout-minutes: 50
    steps:
      - name: git checkout
        uses: actions/checkout@v5
        with:
          ref: ${{ fromJson(inputs.branch).ref }}
      - name: Solaris
        uses: ./.github/actions/solaris
        with:
          configurationParameters: --disable-zts
          runExtraTests: true
  BENCHMARKING:
    name: BENCHMARKING
    if: ${{ fromJson(inputs.branch).jobs.BENCHMARKING }}
    runs-on: ubuntu-${{ fromJson(inputs.branch).config.ubuntu_version }}
    timeout-minutes: 50
    steps:
      - name: git checkout
        uses: actions/checkout@v6
        with:
          ref: ${{ fromJson(inputs.branch).ref }}
          fetch-depth: 0
      # ASLR can cause a lot of noise due to missed sse opportunities for memcpy
      # and other operations, so we disable it during benchmarking.
      - name: Disable ASLR
        run: echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
      - name: apt
        run: |
          set -x
          sudo apt-get update
          sudo apt-get install \
            bison \
            libgmp-dev \
            libonig-dev \
            libsqlite3-dev \
            openssl \
            re2c \
            valgrind
      - name: ccache
        uses: ./.github/actions/ccache
        with:
          name: "${{ github.job }}"
      - name: ./configure
        run: |
          set -x
          ./buildconf --force
          ./configure \
            --disable-debug \
            --enable-mbstring \
            --enable-option-checking=fatal \
            --enable-sockets \
            --enable-werror \
            --prefix=/usr \
            --with-config-file-scan-dir=/etc/php.d \
            --with-gmp \
            --with-mysqli=mysqlnd \
            --with-openssl \
            --with-pdo-sqlite \
            --with-valgrind
      - name: make
        run: make -j$(/usr/bin/nproc) >/dev/null
      - name: make install
        run: |
          set -x
          sudo make install
          sudo mkdir -p /etc/php.d
          sudo chmod 777 /etc/php.d
          echo mysqli.default_socket=/var/run/mysqld/mysqld.sock > /etc/php.d/mysqli.ini
          ${{ !fromJson(inputs.branch).jobs.BENCHMARKING.config.integrated_opcache && 'echo zend_extension=opcache.so >> /etc/php.d/opcache.ini' || '' }}
          echo opcache.enable=1 >> /etc/php.d/opcache.ini
          echo opcache.enable_cli=1 >> /etc/php.d/opcache.ini
      - name: Setup
        run: |
          git config --global user.name "Benchmark"
          git config --global user.email "benchmark@php.net"
          sudo service mysql start
          mysql -uroot -proot -e "CREATE DATABASE IF NOT EXISTS wordpress"
          mysql -uroot -proot -e "CREATE USER 'wordpress'@'localhost' IDENTIFIED BY 'wordpress'; FLUSH PRIVILEGES;"
          mysql -uroot -proot -e "GRANT ALL PRIVILEGES ON *.* TO 'wordpress'@'localhost' WITH GRANT OPTION;"
      - name: git checkout benchmarking-data
        uses: actions/checkout@v6
        with:
          repository: php/benchmarking-data
          ssh-key: ${{ secrets.BENCHMARKING_DATA_DEPLOY_KEY }}
          path: benchmark/repos/data
      - name: Benchmark
        run: php benchmark/benchmark.php true
      - name: Store result
        if: github.event_name == 'push'
        run: |
          set -x
          cd benchmark/repos/data
          git pull --autostash
          if [ -e ".git/MERGE_HEAD" ]; then
            echo "Merging, can't proceed"
            exit 1
          fi
          git add .
          if git diff --cached --quiet; then
            exit 0
          fi
          git commit -m "Add result for ${{ github.repository }}@${{ github.sha }}"
          git push
      - name: Show diff
        if: github.event_name == 'pull_request'
        run: |-
          set -x
          php benchmark/generate_diff.php \
            ${{ github.sha }} \
            ${{ github.event.pull_request.base.sha }} \
            > $GITHUB_STEP_SUMMARY
      - uses: actions/upload-artifact@v6
        with:
          name: profiles
          path: ${{ github.workspace }}/benchmark/profiles
          retention-days: 30
