diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml new file mode 100644 index 000000000..38b3aab12 --- /dev/null +++ b/.github/workflows/code-coverage.yml @@ -0,0 +1,456 @@ +name: Code Coverage + +on: + pull_request: + branches: + - develop + - main + push: + branches: + - develop + - main + workflow_dispatch: + +# Cancels all previous workflow runs for the same branch that have not yet completed. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + code-coverage: + name: Code Coverage Check + runs-on: ubuntu-latest + + services: + mysql: + image: mysql:8.0 + env: + MYSQL_ALLOW_EMPTY_PASSWORD: false + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: wordpress_tests + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=10s --health-retries=10 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Fetch all history for accurate diff + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + ini-values: zend.assertions=1, error_reporting=-1, display_errors=On, memory_limit=512M + coverage: xdebug # Use Xdebug for coverage (PCOV was causing PHP to crash) + tools: composer + + - name: Install SVN + run: sudo apt-get install subversion + + - name: Install Composer dependencies + uses: ramsey/composer-install@v2 + with: + dependency-versions: "highest" + composer-options: "--prefer-dist --with-dependencies" + custom-cache-suffix: $(date -u -d "-0 month -$(($(date +%d)-1)) days" "+%F")-codecov-v2 + + - name: Install WordPress Test Suite + shell: bash + run: tests/bin/install-wp-tests.sh wordpress_tests root root 127.0.0.1:3306 latest + + - name: Generate code coverage report for current branch + run: | + echo "=== Debug: PHP Configuration ===" + php -i | grep -E "(memory_limit|max_execution_time|xdebug)" + echo "=== Debug: Check Xdebug is loaded ===" + php -m | grep xdebug || echo "Xdebug not loaded" + php -r "var_dump(extension_loaded('xdebug'));" + echo "=== Running PHPUnit with coverage ===" + echo "Start time: $(date)" + echo "Memory before: $(free -h | grep Mem)" + + # Run PHPUnit with coverage - allow test failures but ensure coverage is generated + # test-class-security.php is excluded via phpunit.xml.dist to avoid output contamination + set +e + php -d memory_limit=512M -d max_execution_time=300 \ + vendor/bin/phpunit --configuration phpunit.xml.dist \ + --coverage-clover=coverage.xml \ + --coverage-text 2>&1 | tee phpunit-output.log + PHPUNIT_EXIT=$? + set -e + + echo "End time: $(date)" + echo "Memory after: $(free -h | grep Mem)" + echo "=== Debug: PHPUnit exit code: $PHPUNIT_EXIT ===" + echo "=== Note: Exit code $PHPUNIT_EXIT (0=success, 1=test failures, 2=errors, >128=signal termination) ===" + echo "=== Debug: Line count of PHPUnit output ===" + wc -l phpunit-output.log + echo "=== Debug: Last 100 lines of PHPUnit output ===" + tail -100 phpunit-output.log + echo "=== Debug: After running PHPUnit ===" + ls -la coverage* 2>/dev/null || echo "No coverage files in current directory" + echo "=== Checking if coverage report was generated ===" + if [ -f coverage.xml ]; then + echo "SUCCESS: coverage.xml exists!" + ls -lh coverage.xml + echo "First 20 lines of coverage.xml:" + head -20 coverage.xml + else + echo "FAIL: coverage.xml was not generated" + echo "=== Checking for errors in PHPUnit output ===" + grep -i "error\|fatal\|exception\|segfault\|out of memory" phpunit-output.log || echo "No obvious errors found" + # Exit with error if coverage wasn't generated + exit 1 + fi + continue-on-error: false + + - name: Upload PHPUnit output for debugging + if: always() + uses: actions/upload-artifact@v4 + with: + name: phpunit-output + path: phpunit-output.log + retention-days: 7 + + - name: Generate coverage report summary + id: coverage + run: | + # Generate coverage text report and save it + vendor/bin/phpunit --coverage-text --colors=never > current-coverage-full.txt 2>&1 || true + + # Extract overall coverage from the text report (the summary line, not per-class lines) + # Look for the line that starts with " Lines:" (two spaces at the start) + COVERAGE=$(grep "^ Lines:" current-coverage-full.txt | tail -1 | awk '{print $2}' | sed 's/%//' || echo "0") + echo "current_coverage=$COVERAGE" >> $GITHUB_OUTPUT + echo "Current code coverage: $COVERAGE%" + + # Save detailed per-file coverage for later comparison + # PHPUnit outputs class name on one line, stats on the next line + # We need to combine them: "ClassName" + " Methods: X% Lines: Y%" + awk ' + /^[A-Za-z_]/ { classname = $0; next } + /^ Methods:.*Lines:/ { + gsub(/\x1b\[[0-9;]*m/, "", classname); + gsub(/\x1b\[[0-9;]*m/, "", $0); + print classname " " $0 + } + ' current-coverage-full.txt > current-coverage-details.txt || true + + echo "=== Current coverage details saved ===" + head -20 current-coverage-details.txt || true + + - name: Checkout base branch for comparison + if: github.event_name == 'pull_request' + run: | + # Save current branch coverage files + cp current-coverage-details.txt /tmp/current-coverage-details.txt 2>/dev/null || true + cp current-coverage-full.txt /tmp/current-coverage-full.txt 2>/dev/null || true + + # Stash any local changes (like composer.lock) + git stash --include-untracked || true + git fetch origin ${{ github.base_ref }} + git checkout origin/${{ github.base_ref }} + + - name: Install dependencies on base branch + if: github.event_name == 'pull_request' + run: | + composer install --no-interaction --prefer-dist --optimize-autoloader + + - name: Generate coverage report for base branch + if: github.event_name == 'pull_request' + id: base_coverage + run: | + # Generate coverage for base branch + vendor/bin/phpunit --coverage-text --colors=never > base-coverage-full.txt 2>&1 || true + # Extract overall coverage (summary line starting with " Lines:") + BASE_COVERAGE=$(grep "^ Lines:" base-coverage-full.txt | tail -1 | awk '{print $2}' | sed 's/%//' || echo "0") + echo "base_coverage=$BASE_COVERAGE" >> $GITHUB_OUTPUT + echo "Base branch code coverage: $BASE_COVERAGE%" + + # Extract per-file coverage for comparison + # PHPUnit outputs class name on one line, stats on the next line + awk ' + /^[A-Za-z_]/ { classname = $0; next } + /^ Methods:.*Lines:/ { + gsub(/\x1b\[[0-9;]*m/, "", classname); + gsub(/\x1b\[[0-9;]*m/, "", $0); + print classname " " $0 + } + ' base-coverage-full.txt > base-coverage-details.txt || true + + echo "=== Base coverage details saved ===" + head -20 base-coverage-details.txt || true + continue-on-error: true + + - name: Generate coverage diff report + if: github.event_name == 'pull_request' + id: coverage_diff + run: | + # Restore current branch coverage files + cp /tmp/current-coverage-details.txt current-coverage-details.txt 2>/dev/null || true + + # Create a Python script to compare coverage + cat > compare_coverage.py << 'PYTHON_SCRIPT' + import re + import sys + import json + + def parse_coverage_line(line): + """Parse a coverage line to extract class name and line coverage percentage.""" + # Example line: "Progress_Planner\Activity Methods: 55.56% ( 5/ 9) Lines: 91.92% ( 91/ 99)" + match = re.search(r'^([\w\\]+)\s+Methods:\s+([\d.]+)%.*Lines:\s+([\d.]+)%\s+\(\s*(\d+)/\s*(\d+)\)', line) + if match: + class_name = match.group(1) + # Group 2 is methods percentage (not used) + line_percent = float(match.group(3)) # Lines percentage + covered_lines = int(match.group(4)) # Covered lines count + total_lines = int(match.group(5)) # Total lines count + return class_name, line_percent, covered_lines, total_lines + return None, None, None, None + + def load_coverage(filename): + """Load coverage data from file.""" + coverage = {} + try: + with open(filename, 'r') as f: + for line in f: + class_name, percent, covered, total = parse_coverage_line(line) + if class_name: + coverage[class_name] = { + 'percent': percent, + 'covered': covered, + 'total': total + } + except FileNotFoundError: + pass + return coverage + + # Load current and base coverage + current = load_coverage('current-coverage-details.txt') + base = load_coverage('base-coverage-details.txt') + + # Find changes + changes = { + 'new_files': [], + 'improved': [], + 'degraded': [], + 'unchanged': [] + } + + # Check all current files + for class_name in sorted(current.keys()): + curr_data = current[class_name] + if class_name not in base: + # New file + changes['new_files'].append({ + 'class': class_name, + 'coverage': curr_data['percent'], + 'lines': f"{curr_data['covered']}/{curr_data['total']}" + }) + else: + base_data = base[class_name] + diff = curr_data['percent'] - base_data['percent'] + if abs(diff) < 0.01: # Less than 0.01% difference + continue # Skip unchanged files for brevity + elif diff > 0: + changes['improved'].append({ + 'class': class_name, + 'old': base_data['percent'], + 'new': curr_data['percent'], + 'diff': diff + }) + else: + changes['degraded'].append({ + 'class': class_name, + 'old': base_data['percent'], + 'new': curr_data['percent'], + 'diff': diff + }) + + # Output as JSON for GitHub Actions + print(json.dumps(changes)) + PYTHON_SCRIPT + + # Run the comparison + CHANGES_JSON=$(python3 compare_coverage.py) + echo "coverage_changes<> $GITHUB_OUTPUT + echo "$CHANGES_JSON" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + echo "=== Coverage changes ===" + echo "$CHANGES_JSON" | python3 -m json.tool || echo "$CHANGES_JSON" + continue-on-error: true + + - name: Compare coverage and enforce threshold + if: github.event_name == 'pull_request' + run: | + CURRENT="${{ steps.coverage.outputs.current_coverage }}" + BASE="${{ steps.base_coverage.outputs.base_coverage }}" + + # Default to 0 if base coverage couldn't be determined + BASE=${BASE:-0} + + echo "Current Coverage: $CURRENT%" + echo "Base Coverage: $BASE%" + + # Calculate the difference + DIFF=$(echo "$CURRENT - $BASE" | bc) + echo "Coverage Difference: $DIFF%" + + # Check if coverage dropped by more than 0.5% + THRESHOLD=-0.5 + if (( $(echo "$DIFF < $THRESHOLD" | bc -l) )); then + echo "❌ Code coverage dropped by ${DIFF}%, which exceeds the allowed threshold of ${THRESHOLD}%" + echo "Please add tests to maintain or improve code coverage." + exit 1 + else + echo "✅ Code coverage check passed!" + echo "Coverage change: ${DIFF}%" + fi + + - name: Comment PR with coverage + if: github.event_name == 'pull_request' + uses: actions/github-script@v7 + env: + COVERAGE_CHANGES: ${{ steps.coverage_diff.outputs.coverage_changes }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const current = parseFloat('${{ steps.coverage.outputs.current_coverage }}') || 0; + const base = parseFloat('${{ steps.base_coverage.outputs.base_coverage }}') || 0; + const diff = (current - base).toFixed(2); + const diffEmoji = diff >= 0 ? '📈' : '📉'; + const coverageEmoji = current >= 80 ? '🎉' : current >= 60 ? '📈' : current >= 40 ? '📊' : '📉'; + const status = diff >= -0.5 ? '✅' : 'âš ī¸'; + + // Parse coverage changes JSON from environment variable + let changesJson = {}; + try { + const changesStr = process.env.COVERAGE_CHANGES || '{}'; + changesJson = JSON.parse(changesStr); + } catch (e) { + console.log('Failed to parse coverage changes:', e); + console.log('Raw value:', process.env.COVERAGE_CHANGES); + } + + // Build detailed changes section + let detailedChanges = ''; + let hasChanges = false; + + // Build inner content for details + let changesContent = ''; + + // New files with coverage + if (changesJson.new_files && changesJson.new_files.length > 0) { + hasChanges = true; + changesContent += '\n### 🆕 New Files\n\n'; + changesContent += '| Class | Coverage | Lines |\n'; + changesContent += '|-------|----------|-------|\n'; + for (const file of changesJson.new_files) { + const emoji = file.coverage >= 80 ? 'đŸŸĸ' : file.coverage >= 60 ? '🟡' : '🔴'; + changesContent += `| ${emoji} \`${file.class}\` | ${file.coverage.toFixed(2)}% | ${file.lines} |\n`; + } + } + + // Improved coverage + if (changesJson.improved && changesJson.improved.length > 0) { + hasChanges = true; + changesContent += '\n### 📈 Coverage Improved\n\n'; + changesContent += '| Class | Before | After | Change |\n'; + changesContent += '|-------|--------|-------|--------|\n'; + const sortedImproved = changesJson.improved.sort((a, b) => b.diff - a.diff); + for (const file of sortedImproved) { + changesContent += `| \`${file.class}\` | ${file.old.toFixed(2)}% | ${file.new.toFixed(2)}% | +${file.diff.toFixed(2)}% |\n`; + } + } + + // Degraded coverage + if (changesJson.degraded && changesJson.degraded.length > 0) { + hasChanges = true; + changesContent += '\n### 📉 Coverage Decreased\n\n'; + changesContent += '| Class | Before | After | Change |\n'; + changesContent += '|-------|--------|-------|--------|\n'; + const sortedDegraded = changesJson.degraded.sort((a, b) => a.diff - b.diff); + for (const file of sortedDegraded) { + changesContent += `| \`${file.class}\` | ${file.old.toFixed(2)}% | ${file.new.toFixed(2)}% | ${file.diff.toFixed(2)}% |\n`; + } + } + + // Wrap in collapsible details if there are changes + if (hasChanges) { + const totalFiles = (changesJson.new_files?.length || 0) + + (changesJson.improved?.length || 0) + + (changesJson.degraded?.length || 0); + detailedChanges = `\n
\n📊 File-level Coverage Changes (${totalFiles} files)\n${changesContent}\n
\n`; + } + + const comment = `## ${status} Code Coverage Report + + | Metric | Value | + |--------|-------| + | **Total Coverage** | **${current.toFixed(2)}%** ${coverageEmoji} | + | Base Coverage | ${base.toFixed(2)}% | + | Difference | ${diffEmoji} **${diff}%** | + + ${current >= 40 ? '✅ Coverage meets minimum threshold (40%)' : 'âš ī¸ Coverage below recommended 40% threshold'} + + ${diff < -0.5 ? 'âš ī¸ **Warning:** Coverage dropped by more than 0.5%. Please add tests.' : ''} + ${diff >= 0 ? '🎉 Great job maintaining/improving code coverage!' : ''} + + ${detailedChanges} + +
+ â„šī¸ About this report + + - All tests run in a single job with Xdebug coverage + - Security tests excluded from coverage to prevent output issues + - Coverage calculated from line coverage percentages + +
+ `; + + // Find existing coverage report comment + const {data: comments} = await github.rest.issues.listComments({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + }); + + const botComment = comments.find(comment => + comment.user.type === 'Bot' && + comment.body.includes('Code Coverage Report') + ); + + if (botComment) { + // Update existing comment + await github.rest.issues.updateComment({ + comment_id: botComment.id, + owner: context.repo.owner, + repo: context.repo.repo, + body: comment + }); + } else { + // Create new comment + await github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: comment + }); + } + + - name: Generate HTML coverage report + if: always() + run: | + vendor/bin/phpunit --coverage-html=coverage-html + continue-on-error: true + + - name: Upload HTML coverage report as artifact + if: always() + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: coverage-html/ + retention-days: 30 diff --git a/.github/workflows/coverage-status-check.yml b/.github/workflows/coverage-status-check.yml new file mode 100644 index 000000000..af3a7527d --- /dev/null +++ b/.github/workflows/coverage-status-check.yml @@ -0,0 +1,54 @@ +name: Coverage Status Check - DISABLED + +on: + workflow_dispatch: + +jobs: + coverage-gate: + name: Coverage Gate + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Wait for coverage check + timeout-minutes: 10 + run: | + echo "Waiting for Code Coverage Check to complete..." + + # Wait up to 10 minutes for the check to appear and complete + for i in {1..60}; do + # Get the status of the Code Coverage Check + STATUS=$(gh api repos/${{ github.repository }}/commits/${{ github.event.pull_request.head.sha }}/check-runs \ + --jq '.check_runs[] | select(.name == "Code Coverage Check") | .status' || echo "") + + CONCLUSION=$(gh api repos/${{ github.repository }}/commits/${{ github.event.pull_request.head.sha }}/check-runs \ + --jq '.check_runs[] | select(.name == "Code Coverage Check") | .conclusion' || echo "") + + if [ -n "$STATUS" ]; then + echo "Check found with status: $STATUS, conclusion: $CONCLUSION" + + if [ "$STATUS" == "completed" ]; then + if [ "$CONCLUSION" == "success" ]; then + echo "✅ Code coverage check passed!" + exit 0 + else + echo "❌ Code coverage check failed with conclusion: $CONCLUSION" + exit 1 + fi + fi + else + echo "Check not found yet (attempt $i/60)" + fi + + sleep 10 + done + + echo "❌ Timeout waiting for Code Coverage Check" + exit 1 + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Coverage gate passed + run: echo "✅ Code coverage requirements met!" diff --git a/README.md b/README.md index 017274793..6b6e782e5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ [![Test](https://github.com/ProgressPlanner/progress-planner/actions/workflows/phpunit.yml/badge.svg)](https://github.com/ProgressPlanner/progress-planner/actions/workflows/phpunit.yml) +[![Code Coverage](https://github.com/ProgressPlanner/progress-planner/actions/workflows/code-coverage.yml/badge.svg)](https://github.com/ProgressPlanner/progress-planner/actions/workflows/code-coverage.yml) [![CS](https://github.com/ProgressPlanner/progress-planner/actions/workflows/cs.yml/badge.svg)](https://github.com/ProgressPlanner/progress-planner/actions/workflows/cs.yml) [![PHPStan](https://github.com/ProgressPlanner/progress-planner/actions/workflows/phpstan.yml/badge.svg)](https://github.com/ProgressPlanner/progress-planner/actions/workflows/phpstan.yml) [![Lint](https://github.com/ProgressPlanner/progress-planner/actions/workflows/lint.yml/badge.svg)](https://github.com/ProgressPlanner/progress-planner/actions/workflows/lint.yml) @@ -24,6 +25,39 @@ This post explains what Progress Planner does and how to use it: [What does Prog You can find [installation instructions here](https://prpl.fyi/install). +## Contributing + +### Running Tests + +To run the test suite: + +```bash +composer test +``` + +### Code Coverage + +To generate code coverage reports locally, you need either [PCOV](https://pecl.php.net/package/PCOV) (recommended) or [Xdebug](https://xdebug.org/) installed: + +```bash +composer coverage +``` + +This will generate: +- An HTML coverage report in the `coverage-html/` directory +- A text-based coverage summary in your terminal + +**Coverage Requirements:** Pull requests must maintain code coverage within 0.5% of the base branch. PRs that drop coverage by more than 0.5% will be blocked until additional tests are added. + +### Other Quality Commands + +```bash +composer check-cs # Check coding standards +composer fix-cs # Auto-fix coding standards +composer phpstan # Run static analysis +composer lint # Check PHP syntax +``` + ## Branches on this repository We use a couple of branches in this repository to keep things clean: diff --git a/bin/install-wp-tests.sh b/bin/install-wp-tests.sh new file mode 100755 index 000000000..f96bf9ef0 --- /dev/null +++ b/bin/install-wp-tests.sh @@ -0,0 +1,169 @@ +#!/usr/bin/env bash + +if [ $# -lt 3 ]; then + echo "usage: $0 [db-host] [wp-version] [skip-database-creation]" + exit 1 +fi + +DB_NAME=$1 +DB_USER=$2 +DB_PASS=$3 +DB_HOST=${4-localhost} +WP_VERSION=${5-latest} +SKIP_DB_CREATE=${6-false} + +TMPDIR=${TMPDIR-/tmp} +TMPDIR=$(echo $TMPDIR | sed -e "s/\/$//") +WP_TESTS_DIR=${WP_TESTS_DIR-$TMPDIR/wordpress-tests-lib} +WP_CORE_DIR=${WP_CORE_DIR-$TMPDIR/wordpress/} + +download() { + if [ `which curl` ]; then + curl -s "$1" > "$2"; + elif [ `which wget` ]; then + wget -nv -O "$2" "$1" + fi +} + +if [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+\-(beta|RC)[0-9]+$ ]]; then + WP_BRANCH=${WP_VERSION%\-*} + WP_TESTS_TAG="branches/$WP_BRANCH" + +elif [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+$ ]]; then + WP_TESTS_TAG="branches/$WP_VERSION" +elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then + if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then + # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x + WP_TESTS_TAG="tags/${WP_VERSION%??}" + else + WP_TESTS_TAG="tags/$WP_VERSION" + fi +elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then + WP_TESTS_TAG="trunk" +else + # http serves a single offer, whereas https serves multiple. we only want one + download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json + grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json + LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//') + if [[ -z "$LATEST_VERSION" ]]; then + echo "Latest WordPress version could not be found" + exit 1 + fi + WP_TESTS_TAG="tags/$LATEST_VERSION" +fi +set -ex + +install_wp() { + + if [ -d $WP_CORE_DIR ]; then + return; + fi + + mkdir -p $WP_CORE_DIR + + if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then + mkdir -p $TMPDIR/wordpress-trunk + rm -rf $TMPDIR/wordpress-trunk/* + svn export --quiet https://core.svn.wordpress.org/trunk $TMPDIR/wordpress-trunk/wordpress + mv $TMPDIR/wordpress-trunk/wordpress/* $WP_CORE_DIR + else + if [ $WP_VERSION == 'latest' ]; then + local ARCHIVE_NAME='latest' + elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+ ]]; then + # https serves multiple offers, whereas http serves single. + download https://wordpress.org/wordpress-$WP_VERSION.tar.gz $TMPDIR/wordpress.tar.gz + ARCHIVE_NAME="wordpress-$WP_VERSION" + fi + + if [ ! -f $TMPDIR/wordpress.tar.gz ]; then + download https://wordpress.org/latest.tar.gz $TMPDIR/wordpress.tar.gz + fi + tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WP_CORE_DIR + fi + + download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php +} + +install_test_suite() { + # portable in-place argument for both GNU sed and Mac OSX sed + if [[ $(uname -s) == 'Darwin' ]]; then + local ioption='-i.bak' + else + local ioption='-i' + fi + + # set up testing suite if it doesn't yet exist + if [ ! -d $WP_TESTS_DIR ]; then + # set up testing suite + mkdir -p $WP_TESTS_DIR + rm -rf $WP_TESTS_DIR/{includes,data} + svn export --quiet --ignore-externals https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes + svn export --quiet --ignore-externals https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data + fi + + if [ ! -f wp-tests-config.php ]; then + download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php + # remove all forward slashes in the end + WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::") + sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php + fi + +} + +recreate_db() { + shopt -s nocasematch + if [[ $1 =~ ^(y|yes)$ ]] + then + mysqladmin drop $DB_NAME -f --user="$DB_USER" --password="$DB_PASS"$EXTRA + create_db + echo "Recreated the database ($DB_NAME)." + else + echo "Leaving the existing database ($DB_NAME) in place." + fi + shopt -u nocasematch +} + +create_db() { + mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA +} + +install_db() { + + if [ ${SKIP_DB_CREATE} = "true" ]; then + return 0 + fi + + # parse DB_HOST for port or socket references + local PARTS=(${DB_HOST//\:/ }) + local DB_HOSTNAME=${PARTS[0]}; + local DB_SOCK_OR_PORT=${PARTS[1]}; + local EXTRA="" + + if ! [ -z $DB_HOSTNAME ] ; then + if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then + EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp" + elif ! [ -z $DB_SOCK_OR_PORT ] ; then + EXTRA=" --socket=$DB_SOCK_OR_PORT" + elif ! [ -z $DB_HOSTNAME ] ; then + EXTRA=" --host=$DB_HOSTNAME --protocol=tcp" + fi + fi + + # create database + if [ $(mysql --user="$DB_USER" --password="$DB_PASS"$EXTRA --execute='show databases;' | grep ^$DB_NAME$) ] + then + echo "Reinstalling will delete the existing test database ($DB_NAME)" + read -p 'Are you sure you want to proceed? [y/N]: ' DELETE_EXISTING_DB + recreate_db $DELETE_EXISTING_DB + else + create_db + fi +} + +install_wp +install_test_suite +install_db diff --git a/classes/admin/class-page-settings.php b/classes/admin/class-page-settings.php index 7d7d42c8f..8c7c20214 100644 --- a/classes/admin/class-page-settings.php +++ b/classes/admin/class-page-settings.php @@ -58,27 +58,28 @@ public function add_admin_page_content() { public function get_settings() { $settings = []; foreach ( \progress_planner()->get_page_types()->get_page_types() as $page_type ) { - if ( ! $this->should_show_setting( $page_type['slug'] ) ) { + $slug = (string) $page_type['slug']; // @phpstan-ignore offsetAccess.invalidOffset + if ( ! $this->should_show_setting( $slug ) ) { continue; } - $settings[ $page_type['slug'] ] = [ - 'id' => $page_type['slug'], + $settings[ $slug ] = [ + 'id' => $slug, 'value' => '_no_page_needed', 'isset' => 'no', - 'title' => $page_type['title'], - 'description' => $page_type['description'] ?? '', + 'title' => $page_type['title'], // @phpstan-ignore offsetAccess.invalidOffset + 'description' => $page_type['description'] ?? '', // @phpstan-ignore offsetAccess.invalidOffset 'type' => 'page-select', - 'page' => $page_type['slug'], + 'page' => $slug, ]; - if ( \progress_planner()->get_page_types()->is_page_needed( $page_type['slug'] ) ) { - $type_pages = \progress_planner()->get_page_types()->get_posts_by_type( 'any', $page_type['slug'] ); + if ( \progress_planner()->get_page_types()->is_page_needed( $slug ) ) { + $type_pages = \progress_planner()->get_page_types()->get_posts_by_type( 'any', $slug ); if ( empty( $type_pages ) ) { - $settings[ $page_type['slug'] ]['value'] = \progress_planner()->get_page_types()->get_default_page_id_by_type( $page_type['slug'] ); + $settings[ $slug ]['value'] = \progress_planner()->get_page_types()->get_default_page_id_by_type( $slug ); } else { - $settings[ $page_type['slug'] ]['value'] = $type_pages[0]->ID; - $settings[ $page_type['slug'] ]['isset'] = 'yes'; + $settings[ $slug ]['value'] = $type_pages[0]->ID; + $settings[ $slug ]['isset'] = 'yes'; // If there is more than one page, we need to check if the page has a parent with the same page-type assigned. if ( 1 < \count( $type_pages ) ) { @@ -89,7 +90,7 @@ public function get_settings() { foreach ( $type_pages as $type_page ) { $parent = \get_post_field( 'post_parent', $type_page->ID ); if ( $parent && \in_array( (int) $parent, $type_pages_ids, true ) ) { - $settings[ $page_type['slug'] ]['value'] = $parent; + $settings[ $slug ]['value'] = $parent; break; } } diff --git a/classes/admin/widgets/class-activity-scores.php b/classes/admin/widgets/class-activity-scores.php index 974c7abba..8adcde644 100644 --- a/classes/admin/widgets/class-activity-scores.php +++ b/classes/admin/widgets/class-activity-scores.php @@ -98,7 +98,8 @@ public function get_checklist_results() { $items = $this->get_checklist(); $results = []; foreach ( $items as $item ) { - $results[ $item['label'] ] = $item['callback'](); + $label = (string) $item['label']; // @phpstan-ignore offsetAccess.invalidOffset + $results[ $label ] = $item['callback'](); // @phpstan-ignore offsetAccess.invalidOffset } return $results; } diff --git a/classes/class-base.php b/classes/class-base.php index 2e27a3a1a..e31bd8e6b 100644 --- a/classes/class-base.php +++ b/classes/class-base.php @@ -86,10 +86,12 @@ class Base { */ public function init() { if ( ! \function_exists( 'current_user_can' ) ) { - require_once ABSPATH . 'wp-includes/capabilities.php'; // @phpstan-ignore requireOnce.fileNotFound + // @phpstan-ignore-next-line requireOnce.fileNotFound + require_once ABSPATH . 'wp-includes/capabilities.php'; } if ( ! \function_exists( 'wp_get_current_user' ) ) { - require_once ABSPATH . 'wp-includes/pluggable.php'; // @phpstan-ignore requireOnce.fileNotFound + // @phpstan-ignore-next-line requireOnce.fileNotFound + require_once ABSPATH . 'wp-includes/pluggable.php'; } if ( \defined( '\IS_PLAYGROUND_PREVIEW' ) && \constant( '\IS_PLAYGROUND_PREVIEW' ) === true ) { @@ -421,7 +423,8 @@ public function get_file_version( $file ) { // Otherwise, use the plugin header. if ( ! \function_exists( 'get_file_data' ) ) { - require_once ABSPATH . 'wp-includes/functions.php'; // @phpstan-ignore requireOnce.fileNotFound + // @phpstan-ignore-next-line requireOnce.fileNotFound + require_once ABSPATH . 'wp-includes/functions.php'; } if ( ! self::$plugin_version ) { diff --git a/classes/class-suggested-tasks.php b/classes/class-suggested-tasks.php index c79e308ec..e25fc6036 100644 --- a/classes/class-suggested-tasks.php +++ b/classes/class-suggested-tasks.php @@ -479,7 +479,9 @@ public function rest_api_tax_query( $args, $request ) { // Handle sorting parameters. if ( isset( $request['filter']['orderby'] ) ) { - $args['orderby'] = \sanitize_sql_orderby( $request['filter']['orderby'] ); + // @phpstan-ignore-next-line argument.templateType + $orderby = \sanitize_sql_orderby( $request['filter']['orderby'] ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared + $args['orderby'] = $orderby !== false ? $orderby : 'date'; } if ( isset( $request['filter']['order'] ) ) { $args['order'] = \in_array( \strtoupper( $request['filter']['order'] ), [ 'ASC', 'DESC' ], true ) diff --git a/classes/suggested-tasks/class-task.php b/classes/suggested-tasks/class-task.php index 350cea063..c85db2fa6 100644 --- a/classes/suggested-tasks/class-task.php +++ b/classes/suggested-tasks/class-task.php @@ -200,12 +200,14 @@ public function get_rest_formatted_data( $post_id = null ): array { // Make sure WP_REST_Posts_Controller is loaded. if ( ! \class_exists( 'WP_REST_Posts_Controller' ) ) { - require_once ABSPATH . 'wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php'; // @phpstan-ignore requireOnce.fileNotFound + // @phpstan-ignore-next-line requireOnce.fileNotFound + require_once ABSPATH . 'wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php'; } // Make sure WP_REST_Request is loaded. if ( ! \class_exists( 'WP_REST_Request' ) ) { - require_once ABSPATH . 'wp-includes/rest-api/class-wp-rest-request.php'; // @phpstan-ignore requireOnce.fileNotFound + // @phpstan-ignore-next-line requireOnce.fileNotFound + require_once ABSPATH . 'wp-includes/rest-api/class-wp-rest-request.php'; } // Use the appropriate controller for the post type. diff --git a/classes/suggested-tasks/data-collector/class-inactive-plugins.php b/classes/suggested-tasks/data-collector/class-inactive-plugins.php index 2495f48da..abcb26476 100644 --- a/classes/suggested-tasks/data-collector/class-inactive-plugins.php +++ b/classes/suggested-tasks/data-collector/class-inactive-plugins.php @@ -47,7 +47,8 @@ public function update_inactive_plugins_cache() { */ protected function calculate_data() { if ( ! \function_exists( 'get_plugins' ) ) { - require_once ABSPATH . 'wp-admin/includes/plugin.php'; // @phpstan-ignore requireOnce.fileNotFound + // @phpstan-ignore-next-line requireOnce.fileNotFound + require_once ABSPATH . 'wp-admin/includes/plugin.php'; } // Clear the plugins cache, so get_plugins() returns the latest plugins. diff --git a/classes/suggested-tasks/providers/class-core-update.php b/classes/suggested-tasks/providers/class-core-update.php index 714ace08e..0a681b437 100644 --- a/classes/suggested-tasks/providers/class-core-update.php +++ b/classes/suggested-tasks/providers/class-core-update.php @@ -107,7 +107,8 @@ public function add_core_update_link( $update_actions ) { public function should_add_task() { // Without this \wp_get_update_data() might not return correct data for the core updates (depending on the timing). if ( ! \function_exists( 'get_core_updates' ) ) { - require_once ABSPATH . 'wp-admin/includes/update.php'; // @phpstan-ignore requireOnce.fileNotFound + // @phpstan-ignore-next-line requireOnce.fileNotFound + require_once ABSPATH . 'wp-admin/includes/update.php'; } // For wp_get_update_data() to return correct data it needs to be called after the 'admin_init' action (with priority 10). diff --git a/classes/suggested-tasks/providers/class-fewer-tags.php b/classes/suggested-tasks/providers/class-fewer-tags.php index 643e3cb6c..21a929474 100644 --- a/classes/suggested-tasks/providers/class-fewer-tags.php +++ b/classes/suggested-tasks/providers/class-fewer-tags.php @@ -154,7 +154,8 @@ public function is_task_completed( $task_id = '' ) { protected function is_plugin_active() { if ( null === $this->is_plugin_active ) { if ( ! \function_exists( 'get_plugins' ) ) { - require_once ABSPATH . 'wp-admin/includes/plugin.php'; // @phpstan-ignore requireOnce.fileNotFound + // @phpstan-ignore-next-line requireOnce.fileNotFound + require_once ABSPATH . 'wp-admin/includes/plugin.php'; } $plugins = \get_plugins(); diff --git a/classes/suggested-tasks/providers/class-select-locale.php b/classes/suggested-tasks/providers/class-select-locale.php index 88ac6bfcd..313d4aa5c 100644 --- a/classes/suggested-tasks/providers/class-select-locale.php +++ b/classes/suggested-tasks/providers/class-select-locale.php @@ -205,7 +205,8 @@ public function print_popover_instructions() { public function print_popover_form_contents() { if ( ! \function_exists( 'wp_get_available_translations' ) ) { - require_once ABSPATH . 'wp-admin/includes/translation-install.php'; // @phpstan-ignore requireOnce.fileNotFound + // @phpstan-ignore-next-line requireOnce.fileNotFound + require_once ABSPATH . 'wp-admin/includes/translation-install.php'; } $languages = \get_available_languages(); @@ -277,7 +278,8 @@ public function handle_interactive_task_specific_submit() { // Handle translation installation. if ( \current_user_can( 'install_languages' ) ) { - require_once ABSPATH . 'wp-admin/includes/translation-install.php'; // @phpstan-ignore requireOnce.fileNotFound + // @phpstan-ignore-next-line requireOnce.fileNotFound + require_once ABSPATH . 'wp-admin/includes/translation-install.php'; if ( \wp_can_install_language_pack() ) { $language = \wp_download_language_pack( $language_for_update ); diff --git a/classes/ui/class-chart.php b/classes/ui/class-chart.php index 3ae5fc843..abf991136 100644 --- a/classes/ui/class-chart.php +++ b/classes/ui/class-chart.php @@ -125,7 +125,8 @@ public function get_chart_data( $args = [] ) { $previous_period_activities = $period_data['previous_period_activities']; $period_data_filtered = []; foreach ( $args['return_data'] as $key ) { - $period_data_filtered[ $key ] = $period_data[ $key ]; + $key_string = (string) $key; // @phpstan-ignore offsetAccess.invalidOffset + $period_data_filtered[ $key_string ] = $period_data[ $key_string ]; // @phpstan-ignore offsetAccess.invalidOffset } $data[] = $period_data_filtered; } diff --git a/classes/update/class-update-140.php b/classes/update/class-update-140.php index 9a25b2c96..aeb83429c 100644 --- a/classes/update/class-update-140.php +++ b/classes/update/class-update-140.php @@ -41,10 +41,12 @@ private function rename_tasks_option() { // This is to ensure that we don't lose any tasks, and at the same time we don't have duplicate tasks. $tasks = []; foreach ( $new_tasks as $new_task ) { - $tasks[ isset( $new_task['task_id'] ) ? $new_task['task_id'] : \md5( \maybe_serialize( $new_task ) ) ] = $new_task; + $key = isset( $new_task['task_id'] ) ? (string) $new_task['task_id'] : \md5( \maybe_serialize( $new_task ) ); // @phpstan-ignore offsetAccess.invalidOffset + $tasks[ $key ] = $new_task; } foreach ( $old_tasks as $old_task ) { - $tasks[ isset( $old_task['task_id'] ) ? $old_task['task_id'] : \md5( \maybe_serialize( $old_task ) ) ] = $old_task; + $key = isset( $old_task['task_id'] ) ? (string) $old_task['task_id'] : \md5( \maybe_serialize( $old_task ) ); // @phpstan-ignore offsetAccess.invalidOffset + $tasks[ $key ] = $old_task; } // Set the tasks option. diff --git a/classes/utils/class-color-customizer.php b/classes/utils/class-color-customizer.php index 6097b86fb..039d09607 100644 --- a/classes/utils/class-color-customizer.php +++ b/classes/utils/class-color-customizer.php @@ -125,12 +125,13 @@ private function save_colors() { foreach ( $color_variables as $section => $variables ) { foreach ( $variables as $variable => $default_value ) { - $key = "color_{$variable}"; + $variable_string = (string) $variable; // @phpstan-ignore offsetAccess.invalidOffset + $key = "color_{$variable_string}"; if ( isset( $_POST[ $key ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing $post_value = isset( $_POST[ $key ] ) ? \sanitize_text_field( \wp_unslash( $_POST[ $key ] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing $color_value = \sanitize_text_field( \wp_unslash( $post_value ) ); if ( ! empty( $color_value ) ) { - $colors[ $variable ] = $color_value; + $colors[ $variable_string ] = $color_value; } } } diff --git a/composer.json b/composer.json index 294193722..7ff3c28d8 100644 --- a/composer.json +++ b/composer.json @@ -20,6 +20,10 @@ "yoast/yoastcs": "^3.0", "friendsofphp/php-cs-fixer": "^3.75" }, + "suggest": { + "ext-pcov": "Recommended for fast code coverage generation (5x faster than Xdebug)", + "ext-xdebug": "Alternative for code coverage and debugging" + }, "scripts": { "check-cs": [ "@php ./vendor/bin/phpcs -s" @@ -38,6 +42,9 @@ "test": [ "@php ./vendor/phpunit/phpunit/phpunit --dont-report-useless-tests" ], + "coverage": [ + "@php ./vendor/phpunit/phpunit/phpunit --coverage-html=coverage-html --coverage-text" + ], "phpstan": [ "@php ./vendor/bin/phpstan analyse --memory-limit=2048M" ] diff --git a/composer.lock b/composer.lock index ab963d026..1218aae79 100644 --- a/composer.lock +++ b/composer.lock @@ -9,16 +9,16 @@ "packages-dev": [ { "name": "antecedent/patchwork", - "version": "2.2.1", + "version": "2.2.3", "source": { "type": "git", "url": "https://github.com/antecedent/patchwork.git", - "reference": "1bf183a3e1bd094f231a2128b9ecc5363c269245" + "reference": "8b6b235f405af175259c8f56aea5fc23ab9f03ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/antecedent/patchwork/zipball/1bf183a3e1bd094f231a2128b9ecc5363c269245", - "reference": "1bf183a3e1bd094f231a2128b9ecc5363c269245", + "url": "https://api.github.com/repos/antecedent/patchwork/zipball/8b6b235f405af175259c8f56aea5fc23ab9f03ce", + "reference": "8b6b235f405af175259c8f56aea5fc23ab9f03ce", "shasum": "" }, "require": { @@ -51,9 +51,9 @@ ], "support": { "issues": "https://github.com/antecedent/patchwork/issues", - "source": "https://github.com/antecedent/patchwork/tree/2.2.1" + "source": "https://github.com/antecedent/patchwork/tree/2.2.3" }, - "time": "2024-12-11T10:19:54+00:00" + "time": "2025-09-17T09:00:56+00:00" }, { "name": "automattic/vipwpcs", @@ -324,16 +324,16 @@ }, { "name": "composer/semver", - "version": "3.4.3", + "version": "3.4.4", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", - "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", "shasum": "" }, "require": { @@ -385,7 +385,7 @@ "support": { "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.3" + "source": "https://github.com/composer/semver/tree/3.4.4" }, "funding": [ { @@ -395,13 +395,9 @@ { "url": "https://github.com/composer", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" } ], - "time": "2024-09-19T14:15:21+00:00" + "time": "2025-08-20T19:15:30+00:00" }, { "name": "composer/xdebug-handler", @@ -684,16 +680,16 @@ }, { "name": "fidry/cpu-core-counter", - "version": "1.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/theofidry/cpu-core-counter.git", - "reference": "8520451a140d3f46ac33042715115e290cf5785f" + "reference": "db9508f7b1474469d9d3c53b86f817e344732678" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", - "reference": "8520451a140d3f46ac33042715115e290cf5785f", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678", "shasum": "" }, "require": { @@ -703,10 +699,10 @@ "fidry/makefile": "^0.2.0", "fidry/php-cs-fixer-config": "^1.1.2", "phpstan/extension-installer": "^1.2.0", - "phpstan/phpstan": "^1.9.2", - "phpstan/phpstan-deprecation-rules": "^1.0.0", - "phpstan/phpstan-phpunit": "^1.2.2", - "phpstan/phpstan-strict-rules": "^1.4.4", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", "phpunit/phpunit": "^8.5.31 || ^9.5.26", "webmozarts/strict-phpunit": "^7.5" }, @@ -733,7 +729,7 @@ ], "support": { "issues": "https://github.com/theofidry/cpu-core-counter/issues", - "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0" + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0" }, "funding": [ { @@ -741,63 +737,61 @@ "type": "github" } ], - "time": "2024-08-06T10:04:20+00:00" + "time": "2025-08-14T07:29:31+00:00" }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.84.0", + "version": "v3.89.1", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "38dad0767bf2a9b516b976852200ae722fe984ca" + "reference": "f34967da2866ace090a2b447de1f357356474573" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/38dad0767bf2a9b516b976852200ae722fe984ca", - "reference": "38dad0767bf2a9b516b976852200ae722fe984ca", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/f34967da2866ace090a2b447de1f357356474573", + "reference": "f34967da2866ace090a2b447de1f357356474573", "shasum": "" }, "require": { - "clue/ndjson-react": "^1.0", + "clue/ndjson-react": "^1.3", "composer/semver": "^3.4", "composer/xdebug-handler": "^3.0.5", "ext-filter": "*", "ext-hash": "*", "ext-json": "*", "ext-tokenizer": "*", - "fidry/cpu-core-counter": "^1.2", + "fidry/cpu-core-counter": "^1.3", "php": "^7.4 || ^8.0", "react/child-process": "^0.6.6", - "react/event-loop": "^1.0", - "react/promise": "^2.11 || ^3.0", - "react/socket": "^1.0", - "react/stream": "^1.0", + "react/event-loop": "^1.5", + "react/socket": "^1.16", + "react/stream": "^1.4", "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0", - "symfony/console": "^5.4.45 || ^6.4.13 || ^7.0", - "symfony/event-dispatcher": "^5.4.45 || ^6.4.13 || ^7.0", - "symfony/filesystem": "^5.4.45 || ^6.4.13 || ^7.0", - "symfony/finder": "^5.4.45 || ^6.4.17 || ^7.0", - "symfony/options-resolver": "^5.4.45 || ^6.4.16 || ^7.0", - "symfony/polyfill-mbstring": "^1.32", - "symfony/polyfill-php80": "^1.32", - "symfony/polyfill-php81": "^1.32", - "symfony/process": "^5.4.47 || ^6.4.20 || ^7.2", - "symfony/stopwatch": "^5.4.45 || ^6.4.19 || ^7.0" + "symfony/console": "^5.4.47 || ^6.4.24 || ^7.0", + "symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0", + "symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0", + "symfony/finder": "^5.4.45 || ^6.4.24 || ^7.0", + "symfony/options-resolver": "^5.4.45 || ^6.4.24 || ^7.0", + "symfony/polyfill-mbstring": "^1.33", + "symfony/polyfill-php80": "^1.33", + "symfony/polyfill-php81": "^1.33", + "symfony/polyfill-php84": "^1.33", + "symfony/process": "^5.4.47 || ^6.4.24 || ^7.2", + "symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0" }, "require-dev": { - "facile-it/paraunit": "^1.3.1 || ^2.6", - "infection/infection": "^0.29.14", - "justinrainbow/json-schema": "^5.3 || ^6.4", + "facile-it/paraunit": "^1.3.1 || ^2.7", + "infection/infection": "^0.31.0", + "justinrainbow/json-schema": "^6.5", "keradus/cli-executor": "^2.2", "mikey179/vfsstream": "^1.6.12", "php-coveralls/php-coveralls": "^2.8", - "php-cs-fixer/accessible-object": "^1.1", "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", - "phpunit/phpunit": "^9.6.23 || ^10.5.47 || ^11.5.25", - "symfony/polyfill-php84": "^1.32", - "symfony/var-dumper": "^5.4.48 || ^6.4.23 || ^7.3.1", - "symfony/yaml": "^5.4.45 || ^6.4.23 || ^7.3.1" + "phpunit/phpunit": "^9.6.25 || ^10.5.53 || ^11.5.34", + "symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2", + "symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2" }, "suggest": { "ext-dom": "For handling output formats in XML", @@ -838,7 +832,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.84.0" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.89.1" }, "funding": [ { @@ -846,7 +840,7 @@ "type": "github" } ], - "time": "2025-07-15T18:21:57+00:00" + "time": "2025-10-24T12:05:10+00:00" }, { "name": "hamcrest/hamcrest-php", @@ -984,16 +978,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.13.3", + "version": "1.13.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "faed855a7b5f4d4637717c2b3863e277116beb36" + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/faed855a7b5f4d4637717c2b3863e277116beb36", - "reference": "faed855a7b5f4d4637717c2b3863e277116beb36", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", "shasum": "" }, "require": { @@ -1032,7 +1026,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.3" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, "funding": [ { @@ -1040,20 +1034,20 @@ "type": "tidelift" } ], - "time": "2025-07-05T12:25:42+00:00" + "time": "2025-08-01T08:46:24+00:00" }, { "name": "nikic/php-parser", - "version": "v5.6.0", + "version": "v5.6.2", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56" + "reference": "3a454ca033b9e06b63282ce19562e892747449bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/221b0d0fdf1369c71047ad1d18bb5880017bbc56", - "reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb", + "reference": "3a454ca033b9e06b63282ce19562e892747449bb", "shasum": "" }, "require": { @@ -1072,7 +1066,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -1096,9 +1090,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2" }, - "time": "2025-07-27T20:03:57+00:00" + "time": "2025-10-21T19:32:17+00:00" }, { "name": "phar-io/manifest", @@ -1382,16 +1376,16 @@ }, { "name": "php-stubs/wordpress-stubs", - "version": "v6.8.2", + "version": "v6.8.3", "source": { "type": "git", "url": "https://github.com/php-stubs/wordpress-stubs.git", - "reference": "9c8e22e437463197c1ec0d5eaa9ddd4a0eb6d7f8" + "reference": "abeb5a8b58fda7ac21f15ee596f302f2959a7114" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/9c8e22e437463197c1ec0d5eaa9ddd4a0eb6d7f8", - "reference": "9c8e22e437463197c1ec0d5eaa9ddd4a0eb6d7f8", + "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/abeb5a8b58fda7ac21f15ee596f302f2959a7114", + "reference": "abeb5a8b58fda7ac21f15ee596f302f2959a7114", "shasum": "" }, "conflict": { @@ -1427,9 +1421,9 @@ ], "support": { "issues": "https://github.com/php-stubs/wordpress-stubs/issues", - "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.8.2" + "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.8.3" }, - "time": "2025-07-16T06:41:00+00:00" + "time": "2025-09-30T20:58:47+00:00" }, { "name": "phpcompatibility/php-compatibility", @@ -1495,16 +1489,16 @@ }, { "name": "phpcompatibility/phpcompatibility-paragonie", - "version": "1.3.3", + "version": "1.3.4", "source": { "type": "git", "url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git", - "reference": "293975b465e0e709b571cbf0c957c6c0a7b9a2ac" + "reference": "244d7b04fc4bc2117c15f5abe23eb933b5f02bbf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/293975b465e0e709b571cbf0c957c6c0a7b9a2ac", - "reference": "293975b465e0e709b571cbf0c957c6c0a7b9a2ac", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/244d7b04fc4bc2117c15f5abe23eb933b5f02bbf", + "reference": "244d7b04fc4bc2117c15f5abe23eb933b5f02bbf", "shasum": "" }, "require": { @@ -1561,22 +1555,26 @@ { "url": "https://opencollective.com/php_codesniffer", "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcompatibility", + "type": "thanks_dev" } ], - "time": "2024-04-24T21:30:46+00:00" + "time": "2025-09-19T17:43:28+00:00" }, { "name": "phpcompatibility/phpcompatibility-wp", - "version": "2.1.7", + "version": "2.1.8", "source": { "type": "git", "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git", - "reference": "5bfbbfbabb3df2b9a83e601de9153e4a7111962c" + "reference": "7c8d18b4d90dac9e86b0869a608fa09158e168fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/5bfbbfbabb3df2b9a83e601de9153e4a7111962c", - "reference": "5bfbbfbabb3df2b9a83e601de9153e4a7111962c", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/7c8d18b4d90dac9e86b0869a608fa09158e168fa", + "reference": "7c8d18b4d90dac9e86b0869a608fa09158e168fa", "shasum": "" }, "require": { @@ -1638,26 +1636,26 @@ "type": "thanks_dev" } ], - "time": "2025-05-12T16:38:37+00:00" + "time": "2025-10-18T00:05:59+00:00" }, { "name": "phpcsstandards/phpcsextra", - "version": "1.4.0", + "version": "1.4.2", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHPCSExtra.git", - "reference": "fa4b8d051e278072928e32d817456a7fdb57b6ca" + "reference": "8e89a01c7b8fed84a12a2a7f5a23a44cdbe4f62e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/fa4b8d051e278072928e32d817456a7fdb57b6ca", - "reference": "fa4b8d051e278072928e32d817456a7fdb57b6ca", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/8e89a01c7b8fed84a12a2a7f5a23a44cdbe4f62e", + "reference": "8e89a01c7b8fed84a12a2a7f5a23a44cdbe4f62e", "shasum": "" }, "require": { "php": ">=5.4", - "phpcsstandards/phpcsutils": "^1.1.0", - "squizlabs/php_codesniffer": "^3.13.0 || ^4.0" + "phpcsstandards/phpcsutils": "^1.1.2", + "squizlabs/php_codesniffer": "^3.13.4 || ^4.0" }, "require-dev": { "php-parallel-lint/php-console-highlighter": "^1.0", @@ -1720,26 +1718,26 @@ "type": "thanks_dev" } ], - "time": "2025-06-14T07:40:39+00:00" + "time": "2025-10-28T17:00:02+00:00" }, { "name": "phpcsstandards/phpcsutils", - "version": "1.1.0", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHPCSUtils.git", - "reference": "65355670ac17c34cd235cf9d3ceae1b9252c4dad" + "reference": "8b8e17615d04f2fc2cd46fc1d2fd888fa21b3cf9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/65355670ac17c34cd235cf9d3ceae1b9252c4dad", - "reference": "65355670ac17c34cd235cf9d3ceae1b9252c4dad", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/8b8e17615d04f2fc2cd46fc1d2fd888fa21b3cf9", + "reference": "8b8e17615d04f2fc2cd46fc1d2fd888fa21b3cf9", "shasum": "" }, "require": { "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0", "php": ">=5.4", - "squizlabs/php_codesniffer": "^3.13.0 || ^4.0" + "squizlabs/php_codesniffer": "^3.13.3 || ^4.0" }, "require-dev": { "ext-filter": "*", @@ -1813,7 +1811,7 @@ "type": "thanks_dev" } ], - "time": "2025-06-12T04:32:33+00:00" + "time": "2025-10-16T16:39:32+00:00" }, { "name": "phpstan/extension-installer", @@ -1865,16 +1863,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "2.2.0", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8" + "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/b9e61a61e39e02dd90944e9115241c7f7e76bfd8", - "reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/1e0cd5370df5dd2e556a36b9c62f62e555870495", + "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495", "shasum": "" }, "require": { @@ -1906,22 +1904,17 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/2.2.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.0" }, - "time": "2025-07-13T07:04:09+00:00" + "time": "2025-08-30T15:50:23+00:00" }, { "name": "phpstan/phpstan", - "version": "2.1.20", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpstan.git", - "reference": "a9ccfef95210f92ba6feea6e8d1eef42b5605499" - }, + "version": "2.1.31", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/a9ccfef95210f92ba6feea6e8d1eef42b5605499", - "reference": "a9ccfef95210f92ba6feea6e8d1eef42b5605499", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/ead89849d879fe203ce9292c6ef5e7e76f867b96", + "reference": "ead89849d879fe203ce9292c6ef5e7e76f867b96", "shasum": "" }, "require": { @@ -1966,7 +1959,7 @@ "type": "github" } ], - "time": "2025-07-26T20:45:26+00:00" + "time": "2025-10-10T14:14:11+00:00" }, { "name": "phpunit/php-code-coverage", @@ -2289,16 +2282,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.23", + "version": "9.6.29", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95" + "reference": "9ecfec57835a5581bc888ea7e13b51eb55ab9dd3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/43d2cb18d0675c38bd44982a5d1d88f6d53d8d95", - "reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9ecfec57835a5581bc888ea7e13b51eb55ab9dd3", + "reference": "9ecfec57835a5581bc888ea7e13b51eb55ab9dd3", "shasum": "" }, "require": { @@ -2309,7 +2302,7 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.13.1", + "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=7.3", @@ -2320,11 +2313,11 @@ "phpunit/php-timer": "^5.0.3", "sebastian/cli-parser": "^1.0.2", "sebastian/code-unit": "^1.0.8", - "sebastian/comparator": "^4.0.8", + "sebastian/comparator": "^4.0.9", "sebastian/diff": "^4.0.6", "sebastian/environment": "^5.1.5", - "sebastian/exporter": "^4.0.6", - "sebastian/global-state": "^5.0.7", + "sebastian/exporter": "^4.0.8", + "sebastian/global-state": "^5.0.8", "sebastian/object-enumerator": "^4.0.4", "sebastian/resource-operations": "^3.0.4", "sebastian/type": "^3.2.1", @@ -2372,7 +2365,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.23" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.29" }, "funding": [ { @@ -2396,7 +2389,7 @@ "type": "tidelift" } ], - "time": "2025-05-02T06:40:34+00:00" + "time": "2025-09-24T06:29:11+00:00" }, { "name": "psr/container", @@ -2848,23 +2841,23 @@ }, { "name": "react/promise", - "version": "v3.2.0", + "version": "v3.3.0", "source": { "type": "git", "url": "https://github.com/reactphp/promise.git", - "reference": "8a164643313c71354582dc850b42b33fa12a4b63" + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/8a164643313c71354582dc850b42b33fa12a4b63", - "reference": "8a164643313c71354582dc850b42b33fa12a4b63", + "url": "https://api.github.com/repos/reactphp/promise/zipball/23444f53a813a3296c1368bb104793ce8d88f04a", + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a", "shasum": "" }, "require": { "php": ">=7.1.0" }, "require-dev": { - "phpstan/phpstan": "1.10.39 || 1.4.10", + "phpstan/phpstan": "1.12.28 || 1.4.10", "phpunit/phpunit": "^9.6 || ^7.5" }, "type": "library", @@ -2909,7 +2902,7 @@ ], "support": { "issues": "https://github.com/reactphp/promise/issues", - "source": "https://github.com/reactphp/promise/tree/v3.2.0" + "source": "https://github.com/reactphp/promise/tree/v3.3.0" }, "funding": [ { @@ -2917,7 +2910,7 @@ "type": "open_collective" } ], - "time": "2024-05-24T10:39:05+00:00" + "time": "2025-08-19T18:57:03+00:00" }, { "name": "react/socket", @@ -3246,16 +3239,16 @@ }, { "name": "sebastian/comparator", - "version": "4.0.8", + "version": "4.0.9", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + "reference": "67a2df3a62639eab2cc5906065e9805d4fd5dfc5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/67a2df3a62639eab2cc5906065e9805d4fd5dfc5", + "reference": "67a2df3a62639eab2cc5906065e9805d4fd5dfc5", "shasum": "" }, "require": { @@ -3308,15 +3301,27 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.9" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" } ], - "time": "2022-09-14T12:41:17+00:00" + "time": "2025-08-10T06:51:50+00:00" }, { "name": "sebastian/complexity", @@ -3506,16 +3511,16 @@ }, { "name": "sebastian/exporter", - "version": "4.0.6", + "version": "4.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", - "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/14c6ba52f95a36c3d27c835d65efc7123c446e8c", + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c", "shasum": "" }, "require": { @@ -3571,28 +3576,40 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.8" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" } ], - "time": "2024-03-02T06:33:00+00:00" + "time": "2025-09-24T06:03:27+00:00" }, { "name": "sebastian/global-state", - "version": "5.0.7", + "version": "5.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" + "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", - "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/b6781316bdcd28260904e7cc18ec983d0d2ef4f6", + "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6", "shasum": "" }, "require": { @@ -3635,15 +3652,27 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.8" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", + "type": "tidelift" } ], - "time": "2024-03-02T06:35:11+00:00" + "time": "2025-08-10T07:10:35+00:00" }, { "name": "sebastian/lines-of-code", @@ -3816,16 +3845,16 @@ }, { "name": "sebastian/recursion-context", - "version": "4.0.5", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + "reference": "539c6691e0623af6dc6f9c20384c120f963465a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/539c6691e0623af6dc6f9c20384c120f963465a0", + "reference": "539c6691e0623af6dc6f9c20384c120f963465a0", "shasum": "" }, "require": { @@ -3867,15 +3896,27 @@ "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.6" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" } ], - "time": "2023-02-03T06:07:39+00:00" + "time": "2025-08-10T06:57:39+00:00" }, { "name": "sebastian/resource-operations", @@ -4042,28 +4083,27 @@ }, { "name": "sirbrillig/phpcs-variable-analysis", - "version": "v2.12.0", + "version": "v2.13.0", "source": { "type": "git", "url": "https://github.com/sirbrillig/phpcs-variable-analysis.git", - "reference": "4debf5383d9ade705e0a25121f16c3fecaf433a7" + "reference": "a15e970b8a0bf64cfa5e86d941f5e6b08855f369" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sirbrillig/phpcs-variable-analysis/zipball/4debf5383d9ade705e0a25121f16c3fecaf433a7", - "reference": "4debf5383d9ade705e0a25121f16c3fecaf433a7", + "url": "https://api.github.com/repos/sirbrillig/phpcs-variable-analysis/zipball/a15e970b8a0bf64cfa5e86d941f5e6b08855f369", + "reference": "a15e970b8a0bf64cfa5e86d941f5e6b08855f369", "shasum": "" }, "require": { "php": ">=5.4.0", - "squizlabs/php_codesniffer": "^3.5.6" + "squizlabs/php_codesniffer": "^3.5.7 || ^4.0.0" }, "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || ^1.0", - "phpcsstandards/phpcsdevcs": "^1.1", - "phpstan/phpstan": "^1.7", + "phpstan/phpstan": "^1.7 || ^2.0", "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.5 || ^7.0 || ^8.0 || ^9.0 || ^10.5.32 || ^11.3.3", - "vimeo/psalm": "^0.2 || ^0.3 || ^1.1 || ^4.24 || ^5.0" + "vimeo/psalm": "^0.2 || ^0.3 || ^1.1 || ^4.24 || ^5.0 || ^6.0 || ^7.0" }, "type": "phpcodesniffer-standard", "autoload": { @@ -4095,36 +4135,36 @@ "source": "https://github.com/sirbrillig/phpcs-variable-analysis", "wiki": "https://github.com/sirbrillig/phpcs-variable-analysis/wiki" }, - "time": "2025-03-17T16:17:38+00:00" + "time": "2025-09-30T22:22:48+00:00" }, { "name": "slevomat/coding-standard", - "version": "8.20.0", + "version": "8.22.1", "source": { "type": "git", "url": "https://github.com/slevomat/coding-standard.git", - "reference": "b4f9f02edd4e6a586777f0cabe8d05574323f3eb" + "reference": "1dd80bf3b93692bedb21a6623c496887fad05fec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/b4f9f02edd4e6a586777f0cabe8d05574323f3eb", - "reference": "b4f9f02edd4e6a586777f0cabe8d05574323f3eb", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/1dd80bf3b93692bedb21a6623c496887fad05fec", + "reference": "1dd80bf3b93692bedb21a6623c496887fad05fec", "shasum": "" }, "require": { "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.1.2", "php": "^7.4 || ^8.0", - "phpstan/phpdoc-parser": "^2.2.0", - "squizlabs/php_codesniffer": "^3.13.2" + "phpstan/phpdoc-parser": "^2.3.0", + "squizlabs/php_codesniffer": "^3.13.4" }, "require-dev": { "phing/phing": "3.0.1|3.1.0", "php-parallel-lint/php-parallel-lint": "1.4.0", - "phpstan/phpstan": "2.1.19", + "phpstan/phpstan": "2.1.24", "phpstan/phpstan-deprecation-rules": "2.0.3", "phpstan/phpstan-phpunit": "2.0.7", "phpstan/phpstan-strict-rules": "2.0.6", - "phpunit/phpunit": "9.6.8|10.5.48|11.4.4|11.5.27|12.2.7" + "phpunit/phpunit": "9.6.8|10.5.48|11.4.4|11.5.36|12.3.10" }, "type": "phpcodesniffer-standard", "extra": { @@ -4148,7 +4188,7 @@ ], "support": { "issues": "https://github.com/slevomat/coding-standard/issues", - "source": "https://github.com/slevomat/coding-standard/tree/8.20.0" + "source": "https://github.com/slevomat/coding-standard/tree/8.22.1" }, "funding": [ { @@ -4160,20 +4200,20 @@ "type": "tidelift" } ], - "time": "2025-07-26T15:35:10+00:00" + "time": "2025-09-13T08:53:30+00:00" }, { "name": "squizlabs/php_codesniffer", - "version": "3.13.2", + "version": "3.13.4", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "5b5e3821314f947dd040c70f7992a64eac89025c" + "reference": "ad545ea9c1b7d270ce0fc9cbfb884161cd706119" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/5b5e3821314f947dd040c70f7992a64eac89025c", - "reference": "5b5e3821314f947dd040c70f7992a64eac89025c", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/ad545ea9c1b7d270ce0fc9cbfb884161cd706119", + "reference": "ad545ea9c1b7d270ce0fc9cbfb884161cd706119", "shasum": "" }, "require": { @@ -4244,20 +4284,20 @@ "type": "thanks_dev" } ], - "time": "2025-06-17T22:17:01+00:00" + "time": "2025-09-05T05:47:09+00:00" }, { "name": "symfony/console", - "version": "v7.3.1", + "version": "v7.3.5", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "9e27aecde8f506ba0fd1d9989620c04a87697101" + "reference": "cdb80fa5869653c83cfe1a9084a673b6daf57ea7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/9e27aecde8f506ba0fd1d9989620c04a87697101", - "reference": "9e27aecde8f506ba0fd1d9989620c04a87697101", + "url": "https://api.github.com/repos/symfony/console/zipball/cdb80fa5869653c83cfe1a9084a673b6daf57ea7", + "reference": "cdb80fa5869653c83cfe1a9084a673b6daf57ea7", "shasum": "" }, "require": { @@ -4322,7 +4362,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.1" + "source": "https://github.com/symfony/console/tree/v7.3.5" }, "funding": [ { @@ -4333,12 +4373,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-27T19:55:54+00:00" + "time": "2025-10-14T15:46:26+00:00" }, { "name": "symfony/deprecation-contracts", @@ -4409,16 +4453,16 @@ }, { "name": "symfony/event-dispatcher", - "version": "v7.3.0", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "497f73ac996a598c92409b44ac43b6690c4f666d" + "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/497f73ac996a598c92409b44ac43b6690c4f666d", - "reference": "497f73ac996a598c92409b44ac43b6690c4f666d", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b7dc69e71de420ac04bc9ab830cf3ffebba48191", + "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191", "shasum": "" }, "require": { @@ -4469,7 +4513,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.3.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.3.3" }, "funding": [ { @@ -4480,12 +4524,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-22T09:11:45+00:00" + "time": "2025-08-13T11:49:31+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -4565,16 +4613,16 @@ }, { "name": "symfony/filesystem", - "version": "v7.3.0", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb" + "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb", - "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/edcbb768a186b5c3f25d0643159a787d3e63b7fd", + "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd", "shasum": "" }, "require": { @@ -4611,7 +4659,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.3.0" + "source": "https://github.com/symfony/filesystem/tree/v7.3.2" }, "funding": [ { @@ -4622,25 +4670,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-10-25T15:15:23+00:00" + "time": "2025-07-07T08:17:47+00:00" }, { "name": "symfony/finder", - "version": "v7.3.0", + "version": "v7.3.5", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "ec2344cf77a48253bbca6939aa3d2477773ea63d" + "reference": "9f696d2f1e340484b4683f7853b273abff94421f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/ec2344cf77a48253bbca6939aa3d2477773ea63d", - "reference": "ec2344cf77a48253bbca6939aa3d2477773ea63d", + "url": "https://api.github.com/repos/symfony/finder/zipball/9f696d2f1e340484b4683f7853b273abff94421f", + "reference": "9f696d2f1e340484b4683f7853b273abff94421f", "shasum": "" }, "require": { @@ -4675,7 +4727,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.3.0" + "source": "https://github.com/symfony/finder/tree/v7.3.5" }, "funding": [ { @@ -4686,25 +4738,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-12-30T19:00:26+00:00" + "time": "2025-10-15T18:45:57+00:00" }, { "name": "symfony/options-resolver", - "version": "v7.3.0", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "afb9a8038025e5dbc657378bfab9198d75f10fca" + "reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/afb9a8038025e5dbc657378bfab9198d75f10fca", - "reference": "afb9a8038025e5dbc657378bfab9198d75f10fca", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/0ff2f5c3df08a395232bbc3c2eb7e84912df911d", + "reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d", "shasum": "" }, "require": { @@ -4742,7 +4798,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.3.0" + "source": "https://github.com/symfony/options-resolver/tree/v7.3.3" }, "funding": [ { @@ -4753,16 +4809,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-04T13:12:05+00:00" + "time": "2025-08-05T10:16:07+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -4821,7 +4881,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" }, "funding": [ { @@ -4832,6 +4892,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -4841,16 +4905,16 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", "shasum": "" }, "require": { @@ -4899,7 +4963,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" }, "funding": [ { @@ -4910,16 +4974,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-06-27T09:58:17+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -4980,7 +5048,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" }, "funding": [ { @@ -4991,6 +5059,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -5000,7 +5072,7 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", @@ -5061,7 +5133,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" }, "funding": [ { @@ -5072,6 +5144,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -5081,7 +5157,7 @@ }, { "name": "symfony/polyfill-php80", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", @@ -5141,7 +5217,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" }, "funding": [ { @@ -5152,6 +5228,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -5161,7 +5241,7 @@ }, { "name": "symfony/polyfill-php81", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php81.git", @@ -5217,7 +5297,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.33.0" }, "funding": [ { @@ -5228,6 +5308,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -5235,18 +5319,98 @@ ], "time": "2024-09-09T11:45:10+00:00" }, + { + "name": "symfony/polyfill-php84", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php84\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-06-24T13:30:11+00:00" + }, { "name": "symfony/process", - "version": "v7.3.0", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af" + "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", - "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", + "url": "https://api.github.com/repos/symfony/process/zipball/f24f8f316367b30810810d4eb30c543d7003ff3b", + "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b", "shasum": "" }, "require": { @@ -5278,7 +5442,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.3.0" + "source": "https://github.com/symfony/process/tree/v7.3.4" }, "funding": [ { @@ -5289,12 +5453,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-17T09:11:12+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/service-contracts", @@ -5443,16 +5611,16 @@ }, { "name": "symfony/string", - "version": "v7.3.0", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125" + "reference": "f96476035142921000338bad71e5247fbc138872" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f3570b8c61ca887a9e2938e85cb6458515d2b125", - "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125", + "url": "https://api.github.com/repos/symfony/string/zipball/f96476035142921000338bad71e5247fbc138872", + "reference": "f96476035142921000338bad71e5247fbc138872", "shasum": "" }, "require": { @@ -5467,7 +5635,6 @@ }, "require-dev": { "symfony/emoji": "^7.1", - "symfony/error-handler": "^6.4|^7.0", "symfony/http-client": "^6.4|^7.0", "symfony/intl": "^6.4|^7.0", "symfony/translation-contracts": "^2.5|^3.0", @@ -5510,7 +5677,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.0" + "source": "https://github.com/symfony/string/tree/v7.3.4" }, "funding": [ { @@ -5521,25 +5688,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-20T20:19:01+00:00" + "time": "2025-09-11T14:36:48+00:00" }, { "name": "szepeviktor/phpstan-wordpress", - "version": "v2.0.2", + "version": "v2.0.3", "source": { "type": "git", "url": "https://github.com/szepeviktor/phpstan-wordpress.git", - "reference": "963887b04c21fe7ac78e61c1351f8b00fff9f8f8" + "reference": "aa722f037b2d034828cd6c55ebe9e5c74961927e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/szepeviktor/phpstan-wordpress/zipball/963887b04c21fe7ac78e61c1351f8b00fff9f8f8", - "reference": "963887b04c21fe7ac78e61c1351f8b00fff9f8f8", + "url": "https://api.github.com/repos/szepeviktor/phpstan-wordpress/zipball/aa722f037b2d034828cd6c55ebe9e5c74961927e", + "reference": "aa722f037b2d034828cd6c55ebe9e5c74961927e", "shasum": "" }, "require": { @@ -5549,6 +5720,7 @@ }, "require-dev": { "composer/composer": "^2.1.14", + "composer/semver": "^3.4", "dealerdirect/phpcodesniffer-composer-installer": "^1.0", "php-parallel-lint/php-parallel-lint": "^1.1", "phpstan/phpstan-strict-rules": "^2.0", @@ -5586,9 +5758,9 @@ ], "support": { "issues": "https://github.com/szepeviktor/phpstan-wordpress/issues", - "source": "https://github.com/szepeviktor/phpstan-wordpress/tree/v2.0.2" + "source": "https://github.com/szepeviktor/phpstan-wordpress/tree/v2.0.3" }, - "time": "2025-02-12T18:43:37+00:00" + "time": "2025-09-14T02:58:22+00:00" }, { "name": "theseer/tokenizer", @@ -5708,16 +5880,16 @@ }, { "name": "yoast/phpunit-polyfills", - "version": "1.1.4", + "version": "1.1.5", "source": { "type": "git", "url": "https://github.com/Yoast/PHPUnit-Polyfills.git", - "reference": "e6faedf5e34cea4438e341f660e2f719760c531d" + "reference": "41aaac462fbd80feb8dd129e489f4bbc53fe26b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/e6faedf5e34cea4438e341f660e2f719760c531d", - "reference": "e6faedf5e34cea4438e341f660e2f719760c531d", + "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/41aaac462fbd80feb8dd129e489f4bbc53fe26b0", + "reference": "41aaac462fbd80feb8dd129e489f4bbc53fe26b0", "shasum": "" }, "require": { @@ -5727,7 +5899,7 @@ "require-dev": { "php-parallel-lint/php-console-highlighter": "^1.0.0", "php-parallel-lint/php-parallel-lint": "^1.4.0", - "yoast/yoastcs": "^3.1.0" + "yoast/yoastcs": "^3.2.0" }, "type": "library", "extra": { @@ -5767,7 +5939,7 @@ "security": "https://github.com/Yoast/PHPUnit-Polyfills/security/policy", "source": "https://github.com/Yoast/PHPUnit-Polyfills" }, - "time": "2025-02-09T18:13:44+00:00" + "time": "2025-08-10T04:54:36+00:00" }, { "name": "yoast/wp-test-utils", diff --git a/phpcs.xml.dist b/phpcs.xml.dist index d2d05322a..69a6fdc2c 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -147,4 +147,12 @@ /tests/bootstrap\.php$ + + + /tests/phpunit/ + + + + /tests/phpunit/ + diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 7ce1df6be..1d131bfe6 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -10,12 +10,13 @@ ./tests/ + ./tests/phpunit/test-class-security.php - - - src - progress-planner.php - - + + + classes + progress-planner.php + + diff --git a/tests/bin/install-wp-tests.sh b/tests/bin/install-wp-tests.sh index 7cab84523..66acf2a47 100755 --- a/tests/bin/install-wp-tests.sh +++ b/tests/bin/install-wp-tests.sh @@ -193,8 +193,14 @@ install_db() { if [ $(mysql --user="$DB_USER" --password="$DB_PASS"$EXTRA --execute='show databases;' | grep ^$DB_NAME$) ] then echo "Reinstalling will delete the existing test database ($DB_NAME)" - read -p 'Are you sure you want to proceed? [y/N]: ' DELETE_EXISTING_DB - recreate_db $DELETE_EXISTING_DB + # In CI environments, automatically proceed without prompting + if [ -n "$CI" ] || [ -n "$GITHUB_ACTIONS" ]; then + echo "CI environment detected, automatically recreating database..." + recreate_db "y" + else + read -p 'Are you sure you want to proceed? [y/N]: ' DELETE_EXISTING_DB + recreate_db $DELETE_EXISTING_DB + fi else create_db fi