diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 5be56bd..7016929 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -45,6 +45,9 @@ jobs: - name: Run linting run: composer run-script lint + - name: Run static analysis + run: composer run-script phpstan + - name: Run test suite run: composer run-script test diff --git a/composer.json b/composer.json index 70a0c1c..8569fdb 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,8 @@ "require-dev": { "phpunit/phpunit": "^13", "friendsofphp/php-cs-fixer": "^3.94", - "php-coveralls/php-coveralls": "^2.9" + "php-coveralls/php-coveralls": "^2.9", + "phpstan/phpstan": "^2.1" }, "autoload": { "psr-4": { @@ -30,6 +31,7 @@ "scripts": { "lint": "vendor/bin/php-cs-fixer -vvv check --using-cache=no", "lint-fix": "vendor/bin/php-cs-fixer -vvv fix --using-cache=no", + "phpstan": "vendor/bin/phpstan", "test": "XDEBUG_MODE=coverage vendor/bin/phpunit", "serve-examples": "@php -S localhost:8080 -t examples" }, diff --git a/composer.lock b/composer.lock index 81aea7b..789a78c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b99f580de2abf676796df50ae6addd59", + "content-hash": "edc273ca7a74db1ddeb047d4e20e7351", "packages": [], "packages-dev": [ { @@ -1150,6 +1150,59 @@ }, "time": "2025-12-18T13:08:37+00:00" }, + { + "name": "phpstan/phpstan", + "version": "2.1.42", + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/1279e1ce86ba768f0780c9d889852b4e02ff40d0", + "reference": "1279e1ce86ba768f0780c9d889852b4e02ff40d0", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + } + ], + "time": "2026-03-17T14:58:32+00:00" + }, { "name": "phpunit/php-code-coverage", "version": "13.0.1", diff --git a/examples/appengine-https.php b/examples/appengine-https.php index cee9162..7ef8474 100644 --- a/examples/appengine-https.php +++ b/examples/appengine-https.php @@ -37,7 +37,13 @@ if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) { if ('http' === $_SERVER['HTTP_X_FORWARDED_PROTO']) { header('HTTP/1.1 301 Moved Permanently'); - header('Location: https://'.$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI']); + + /** @var string $serverName */ + $serverName = $_SERVER['SERVER_NAME']; + + /** @var string $requestUri */ + $requestUri = $_SERVER['REQUEST_URI']; + header('Location: https://'.$serverName.$requestUri); exit(0); } diff --git a/examples/recaptcha-content-security-policy.php b/examples/recaptcha-content-security-policy.php index 6498f5e..e4e427f 100644 --- a/examples/recaptcha-content-security-policy.php +++ b/examples/recaptcha-content-security-policy.php @@ -70,7 +70,8 @@ $secret = ''; // Copy the config.php.dist file to config.php and update it with your keys to run the examples -if ('' == $siteKey && is_readable(__DIR__.'/config.php')) { +if (is_readable(__DIR__.'/config.php')) { + /** @var array{v3: array{site: string, secret: string}} $config */ $config = include __DIR__.'/config.php'; $siteKey = $config['v3']['site']; $secret = $config['v3']['secret']; @@ -115,7 +116,7 @@

NOTE:This is a sample implementation, the score returned here is not a reflection on your Google account or type of traffic. In production, refer to the distribution of scores shown in your admin interface and adjust your own threshold accordingly. Do not raise issues regarding the score you see here.

  1. reCAPTCHA script loading
  2. - + @@ -128,7 +129,7 @@ const steps = document.getElementById('recaptcha-steps'); grecaptcha.ready(function() { document.querySelector('.step1').classList.remove('hidden'); - grecaptcha.execute('', {action: ''}).then(function(token) { + grecaptcha.execute('', {action: ''}).then(function(token) { document.querySelector('.token').innerHTML = 'fetch(\'/recaptcha-v3-verify.php?action=&token=\'' + token; document.querySelector('.step2').classList.remove('hidden'); @@ -143,7 +144,7 @@ }; - + diff --git a/examples/recaptcha-v2-checkbox-explicit.php b/examples/recaptcha-v2-checkbox-explicit.php index 1cf637f..3393c68 100644 --- a/examples/recaptcha-v2-checkbox-explicit.php +++ b/examples/recaptcha-v2-checkbox-explicit.php @@ -47,7 +47,8 @@ $secret = ''; // Copy the config.php.dist file to config.php and update it with your keys to run the examples -if ('' == $siteKey && is_readable(__DIR__.'/config.php')) { +if (is_readable(__DIR__.'/config.php')) { + /** @var array{'v2-standard': array{site: string, secret: string}} $config */ $config = include __DIR__.'/config.php'; $siteKey = $config['v2-standard']['site']; $secret = $config['v2-standard']['secret']; @@ -91,6 +92,7 @@ setExpectedHostname($_SERVER['SERVER_NAME']) - ->verify($_POST[ReCaptcha::RESPONSE_KEY], $_SERVER['REMOTE_ADDR']) + /** @var string $serverName */ + $serverName = $_SERVER['SERVER_NAME']; + + /** @var string $responseKey */ + $responseKey = $_POST[ReCaptcha::RESPONSE_KEY]; + + /** @var null|string $remoteAddr */ + $remoteAddr = $_SERVER['REMOTE_ADDR']; + + $resp = $recaptcha->setExpectedHostname($serverName) + ->verify($responseKey, $remoteAddr) ; if ($resp->isSuccess()) { @@ -139,7 +150,7 @@ var onloadCallback = function() { var captchaContainer = document.querySelector('.g-recaptcha'); grecaptcha.render(captchaContainer, { - 'sitekey' : '' + 'sitekey' : '' }); document.querySelector('button[type="submit"]').disabled = false; }; diff --git a/examples/recaptcha-v2-checkbox.php b/examples/recaptcha-v2-checkbox.php index 2031946..25ad3fd 100644 --- a/examples/recaptcha-v2-checkbox.php +++ b/examples/recaptcha-v2-checkbox.php @@ -47,7 +47,8 @@ $secret = ''; // Copy the config.php.dist file to config.php and update it with your keys to run the examples -if ('' == $siteKey && is_readable(__DIR__.'/config.php')) { +if (is_readable(__DIR__.'/config.php')) { + /** @var array{'v2-standard': array{site: string, secret: string}} $config */ $config = include __DIR__.'/config.php'; $siteKey = $config['v2-standard']['site']; $secret = $config['v2-standard']['secret']; @@ -91,6 +92,7 @@ setExpectedHostname($_SERVER['SERVER_NAME']) - ->verify($_POST[ReCaptcha::RESPONSE_KEY], $_SERVER['REMOTE_ADDR']) + /** @var string $serverName */ + $serverName = $_SERVER['SERVER_NAME']; + + /** @var string $responseKey */ + $responseKey = $_POST[ReCaptcha::RESPONSE_KEY]; + + /** @var null|string $remoteAddr */ + $remoteAddr = $_SERVER['REMOTE_ADDR']; + + $resp = $recaptcha->setExpectedHostname($serverName) + ->verify($responseKey, $remoteAddr) ; if ($resp->isSuccess()) { // If the response is a success, that's it! @@ -130,7 +141,7 @@ -
    +
    diff --git a/examples/recaptcha-v2-invisible.php b/examples/recaptcha-v2-invisible.php index 2552c55..f075e6b 100644 --- a/examples/recaptcha-v2-invisible.php +++ b/examples/recaptcha-v2-invisible.php @@ -47,7 +47,8 @@ $secret = ''; // Copy the config.php.dist file to config.php and update it with your keys to run the examples -if ('' == $siteKey && is_readable(__DIR__.'/config.php')) { +if (is_readable(__DIR__.'/config.php')) { + /** @var array{'v2-invisible': array{site: string, secret: string}} $config */ $config = include __DIR__.'/config.php'; $siteKey = $config['v2-invisible']['site']; $secret = $config['v2-invisible']['secret']; @@ -91,6 +92,7 @@ setExpectedHostname($_SERVER['SERVER_NAME']) - ->verify($_POST[ReCaptcha::RESPONSE_KEY], $_SERVER['REMOTE_ADDR']) + /** @var string $serverName */ + $serverName = $_SERVER['SERVER_NAME']; + + /** @var string $responseKey */ + $responseKey = $_POST[ReCaptcha::RESPONSE_KEY]; + + /** @var null|string $remoteAddr */ + $remoteAddr = $_SERVER['REMOTE_ADDR']; + + $resp = $recaptcha->setExpectedHostname($serverName) + ->verify($responseKey, $remoteAddr) ; if ($resp->isSuccess()) { // If the response is a success, that's it! @@ -129,7 +140,7 @@ An example form - + diff --git a/examples/recaptcha-v3-request-scores.php b/examples/recaptcha-v3-request-scores.php index d92a7ee..80368b0 100644 --- a/examples/recaptcha-v3-request-scores.php +++ b/examples/recaptcha-v3-request-scores.php @@ -44,7 +44,8 @@ $secret = ''; // Copy the config.php.dist file to config.php and update it with your keys to run the examples -if ('' == $siteKey && is_readable(__DIR__.'/config.php')) { +if (is_readable(__DIR__.'/config.php')) { + /** @var array{v3: array{site: string, secret: string}} $config */ $config = include __DIR__.'/config.php'; $siteKey = $config['v3']['site']; $secret = $config['v3']['secret']; @@ -90,18 +91,18 @@

    NOTE:This is a sample implementation, the score returned here is not a reflection on your Google account or type of traffic. In production, refer to the distribution of scores shown in your admin interface and adjust your own threshold accordingly. Do not raise issues regarding the score you see here.

    1. reCAPTCHA script loading
    2. - +

    Try again

    - +