Skip to content

Refactor the requirements check #971

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 210 additions & 0 deletions .github/workflows/test-requirements-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
name: Test requirements check

on:
# Run on pushes to the main branches and on pull requests which touch files related to the requirements check.
# No need to run this workflow when there are only irrelevant changes.
push:
branches:
- 4.x
tags:
- '**'
paths:
- '.github/workflows/test-requirements-check.yml'
- '.github/workflows/reusable-build-phar.yml'
- 'bin/phpcs'
- 'bin/phpcbf'
- 'phpcs.xml.dist'
- 'requirements.php'
- 'scripts/**'
pull_request:
paths:
- '.github/workflows/test-requirements-check.yml'
- '.github/workflows/reusable-build-phar.yml'
- 'bin/phpcs'
- 'bin/phpcbf'
- 'phpcs.xml.dist'
- 'requirements.php'
- 'scripts/**'

# Allow manually triggering the workflow.
workflow_dispatch:

# Cancels all previous workflow runs for the same branch that have not yet completed.
concurrency:
# The concurrency group contains the workflow name and the branch name.
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
# Make sure that the files involved in the requirements check don't contain parse errors
# for the PHP versions supported by the requirements check to prevent the tests being run
# failing on the parse errors instead of on the requirements check
# (which would easily go unnoticed).
lint:
runs-on: ubuntu-latest

strategy:
matrix:
php: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1']

name: "Lint: PHP ${{ matrix.php }}"

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: none

- name: "Lint bin/phpcs"
run: php -l ./bin/phpcs

- name: "Lint bin/phpcbf"
run: php -l ./bin/phpcbf

- name: "Lint requirements.php"
run: php -l ./requirements.php

# The matrix for these tests should be the same for the "plain" file test as for the PHAR test.
# This matrix, however, is quite complex, making maintaining it manually pretty error prone.
# So this job uses a PHP script to generate the matrix based on a fixed set of variables.
#
# The resulting matrix contains builds which combine the following variables:
# - os: ubuntu / windows
# - cmd: phpcs / phpcbf
# - php: 7.2 (minimum PHP version for 4.x), latest and nightly with the required extensions (should pass the check).
# - php: 5.3 (minimum for the requirements check) and 7.1 with the required extension (should fail the PHP version check).
# - php: 7.2 (minimum PHP version for 4.x), latest and nightly WITHOUT required extensions (should fail the check for extensions).
#
# Each combination also contains a "expect" key to set the expectations for success / failure.
#
# The scripts involved in generating the matrix can be found in the `/scripts/` directory.
prepare-matrix:
needs: lint

name: Get test matrix
runs-on: ubuntu-latest

outputs:
matrix: ${{ steps.set-matrix.outputs.MATRIX }}

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: 'latest'
ini-values: 'error_reporting=-1, display_errors=On'
coverage: none

- name: Set matrix
id: set-matrix
run: echo "MATRIX=$(php scripts/get-requirements-check-matrix.php)" >> "$GITHUB_OUTPUT"

- name: "DEBUG: show generated matrix"
run: echo ${{ steps.set-matrix.outputs.MATRIX }}

# Test that the requirements check works correctly when using a Composer install or git clone of PHPCS.
test-plain:
needs: prepare-matrix

strategy:
fail-fast: false
matrix: ${{ fromJson(needs.prepare-matrix.outputs.MATRIX) }}

# yamllint disable-line rule:line-length
name: "Plain: ${{ matrix.cmd == 'phpcs' && 'cs' || 'cbf' }} / ${{ matrix.php }} / ${{ matrix.name }} (${{ matrix.os == 'ubuntu-latest' && 'nix' || 'Win' }})"

continue-on-error: ${{ matrix.php == 'nightly' }}

runs-on: ${{ matrix.os }}

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
ini-values: 'error_reporting=-1, display_errors=On'
extensions: ${{ matrix.extensions }}
coverage: none
env:
fail-fast: true

# We're only testing the requirements check here, so we just need to verify that PHPCS/PHPCBF starts. Nothing more.
- name: Run the test
id: check
continue-on-error: true
run: php "bin/${{ matrix.cmd }}" --version

- name: Check the result of a successful test against expectation
if: ${{ steps.check.outcome == 'success' && matrix.expect == 'fail' }}
run: exit 1

- name: Check the result of a failed test against expectation
if: ${{ steps.check.outcome != 'success' && matrix.expect == 'success' }}
run: exit 1

build-phars:
needs: lint

name: "Build Phar on PHP 8.0"

uses: ./.github/workflows/reusable-build-phar.yml
with:
uploadArtifacts: true

# Test that the requirements check works correctly when using a PHPCS/PHPCBF PHAR file.
test-phar:
needs: [prepare-matrix, build-phars]

strategy:
fail-fast: false
matrix: ${{ fromJson(needs.prepare-matrix.outputs.MATRIX) }}

# yamllint disable-line rule:line-length
name: "PHAR: ${{ matrix.cmd == 'phpcs' && 'cs' || 'cbf' }} / ${{ matrix.php }} / ${{ matrix.name }} (${{ matrix.os == 'ubuntu-latest' && 'nix' || 'Win' }})"

continue-on-error: ${{ matrix.php == 'nightly' }}

runs-on: ${{ matrix.os }}

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
ini-values: 'error_reporting=-1, display_errors=On'
extensions: ${{ matrix.extensions }}
coverage: none
env:
fail-fast: true

- name: Download the phar
uses: actions/download-artifact@v4
with:
name: ${{ matrix.cmd }}-phar

# We're only testing the requirements check here, so we just need to verify that PHPCS/PHPCBF starts. Nothing more.
- name: Run the test
id: check
continue-on-error: true
run: php ${{ matrix.cmd }}.phar --version

- name: Check the result of a successful test against expectation
if: ${{ steps.check.outcome == 'success' && matrix.expect == 'fail' }}
run: exit 1

- name: Check the result of a failed test against expectation
if: ${{ steps.check.outcome != 'success' && matrix.expect == 'success' }}
run: exit 1
18 changes: 17 additions & 1 deletion bin/phpcbf
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,28 @@
/**
* PHP Code Beautifier and Fixer fixes violations of a defined coding standard.
*
* :WARNING:
* This file MUST stay cross-version compatible with older PHP versions (min: PHP 5.3) to allow
* for the requirements check to work correctly.
*
* The PHP 5.3 minimum is set as the previous PHPCS major (3.x) already had a PHP 5.4 minimum
* requirement and didn't take parse errors caused due to the use of namespaces into account
* in its requirements check, so running PHPCS 3.x on PHP < 5.3 would have failed with a parse
* error already anyway, so PHP 5.3 seems reasonable to keep as the minimum for this.
* :WARNING:
*
* @author Greg Sherwood <[email protected]>
* @author Juliette Reinders Folmer <[email protected]>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @copyright 2024 PHPCSStandards and contributors
* @license https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/

require_once __DIR__.'/../autoload.php';
// Check if the PHP version and extensions comply with the minimum requirements before anything else.
require_once dirname(__DIR__).'/requirements.php';
PHP_CodeSniffer\checkRequirements();

require_once dirname(__DIR__).'/autoload.php';

$runner = new PHP_CodeSniffer\Runner();
$exitCode = $runner->runPHPCBF();
Expand Down
18 changes: 17 additions & 1 deletion bin/phpcs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,28 @@
/**
* PHP_CodeSniffer detects violations of a defined coding standard.
*
* :WARNING:
* This file MUST stay cross-version compatible with older PHP versions (min: PHP 5.3) to allow
* for the requirements check to work correctly.
*
* The PHP 5.3 minimum is set as the previous PHPCS major (3.x) already had a PHP 5.4 minimum
* requirement and didn't take parse errors caused due to the use of namespaces into account
* in its requirements check, so running PHPCS 3.x on PHP < 5.3 would have failed with a parse
* error already anyway, so PHP 5.3 seems reasonable to keep as the minimum for this.
* :WARNING:
*
* @author Greg Sherwood <[email protected]>
* @author Juliette Reinders Folmer <[email protected]>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @copyright 2024 PHPCSStandards and contributors
* @license https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/

require_once __DIR__.'/../autoload.php';
// Check if the PHP version and extensions comply with the minimum requirements before anything else.
require_once dirname(__DIR__).'/requirements.php';
PHP_CodeSniffer\checkRequirements();

require_once dirname(__DIR__).'/autoload.php';

$runner = new PHP_CodeSniffer\Runner();
$exitCode = $runner->runPHPCS();
Expand Down
7 changes: 6 additions & 1 deletion phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<description>The coding standard for PHP_CodeSniffer itself.</description>

<file>autoload.php</file>
<file>requirements.php</file>
<file>bin</file>
<file>scripts</file>
<file>src</file>
Expand Down Expand Up @@ -58,7 +59,11 @@
<rule ref="Squiz.WhiteSpace.MemberVarSpacing"/>
<rule ref="Squiz.WhiteSpace.OperatorSpacing"/>
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace"/>
<rule ref="Generic.Arrays.DisallowLongArraySyntax"/>
<rule ref="Generic.Arrays.DisallowLongArraySyntax">
<!-- Files involved in the requirements check should stay compatible with PHP 5.3. -->
<exclude-pattern>bin/phpc(s|bf)$</exclude-pattern>
<exclude-pattern>requirements\.php$</exclude-pattern>
</rule>
<rule ref="Generic.Commenting.Todo"/>
<rule ref="Generic.ControlStructures.DisallowYodaConditions"/>
<rule ref="Generic.ControlStructures.InlineControlStructure"/>
Expand Down
1 change: 1 addition & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ parameters:
max: 80499
level: 0
paths:
- requirements.php
- src
bootstrapFiles:
- tests/bootstrap.php
Expand Down
71 changes: 71 additions & 0 deletions requirements.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php
/**
* Requirements check for PHP_CodeSniffer.
*
* :WARNING:
* This file MUST stay cross-version compatible with older PHP versions (min: PHP 5.3) to allow
* for the requirements check to work correctly.
*
* The PHP 5.3 minimum is set as the previous PHPCS major (3.x) already had a PHP 5.4 minimum
* requirement and didn't take parse errors caused due to the use of namespaces into account
* in its requirements check, so running PHPCS 3.x on PHP < 5.3 would have failed with a parse
* error already anyway, so PHP 5.3 seems reasonable to keep as the minimum for this.
* :WARNING:
*
* @author Greg Sherwood <[email protected]>
* @author Juliette Reinders Folmer <[email protected]>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @copyright 2024 PHPCSStandards and contributors
* @license https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/

namespace PHP_CodeSniffer;


/**
* Exits if the minimum requirements of PHP_CodeSniffer are not met.
*
* @return void
*/
function checkRequirements()
{
$exitCode = 3;

// Check the PHP version.
if (PHP_VERSION_ID < 70200) {
echo 'ERROR: PHP_CodeSniffer requires PHP version 7.2.0 or greater.'.PHP_EOL;
exit($exitCode);
}

$requiredExtensions = array(
'tokenizer',
'xmlwriter',
'SimpleXML',
);
$missingExtensions = array();

foreach ($requiredExtensions as $extension) {
if (extension_loaded($extension) === false) {
$missingExtensions[] = $extension;
}
}

if (empty($missingExtensions) === false) {
$last = array_pop($requiredExtensions);
$required = implode(', ', $requiredExtensions);
$required .= ' and '.$last;

if (count($missingExtensions) === 1) {
$missing = $missingExtensions[0];
} else {
$last = array_pop($missingExtensions);
$missing = implode(', ', $missingExtensions);
$missing .= ' and '.$last;
}

$error = 'ERROR: PHP_CodeSniffer requires the %s extensions to be enabled. Please enable %s.'.PHP_EOL;
printf($error, $required, $missing);
exit($exitCode);
}

}//end checkRequirements()
Loading
Loading