diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..b01043c
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,11 @@
+version: 2
+updates:
+ -
+ package-ecosystem: composer
+ directory: "/"
+ schedule:
+ interval: monthly
+ versioning-strategy: auto
+ groups:
+ dev-dependencies:
+ dependency-type: "development"
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index f3b7f28..879203e 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -8,11 +8,11 @@ jobs:
strategy:
matrix:
- php: [8.0, 8.1]
+ php: [8.1, 8.2, 8.3]
steps:
- name: Checkout code
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
@@ -26,6 +26,12 @@ jobs:
- name: Install dependencies
run: composer install --prefer-dist --no-progress --no-interaction --no-suggest
+ - name: Execute Code Sniffer
+ run: vendor/bin/phpcs
+
+ - name: Execute PHP Stan
+ run: vendor/bin/phpstan
+
- name: Run test suite
run: |
php -S 127.0.0.1:8000 -t tests/data/app >/dev/null 2>&1 &
diff --git a/composer.json b/composer.json
index a9c2509..ce86167 100644
--- a/composer.json
+++ b/composer.json
@@ -18,17 +18,19 @@
],
"homepage": "https://codeception.com/",
"require": {
- "php": "^8.0",
+ "php": "^8.1",
"ext-json": "*",
"codeception/codeception": "*@dev",
"codeception/lib-innerbrowser": "*@dev",
"guzzlehttp/guzzle": "^7.4",
- "symfony/browser-kit": "^5.4 || ^6.0 || ^7.0"
+ "symfony/browser-kit": "^5.4 | ^6.0 | ^7.0"
},
"require-dev": {
"ext-curl": "*",
+ "squizlabs/php_codesniffer": "^3.10",
+ "phpstan/phpstan": "^1.10",
"aws/aws-sdk-php": "^3.199",
- "codeception/module-rest": "^2.0 || *@dev"
+ "codeception/module-rest": "^2.0 | *@dev"
},
"conflict": {
"codeception/codeception": "<5.0",
@@ -37,7 +39,6 @@
"suggest": {
"codeception/phpbuiltinserver": "Start and stop PHP built-in web server for your tests"
},
- "minimum-stability": "dev",
"autoload": {
"classmap": [
"src/"
diff --git a/phpcs.xml b/phpcs.xml
new file mode 100644
index 0000000..7b36367
--- /dev/null
+++ b/phpcs.xml
@@ -0,0 +1,23 @@
+
+
+ The coding standard.
+
+
+
+ src
+
+
+
+
+
+
+
+ error
+
+
+
+
+
+ warning
+
+
\ No newline at end of file
diff --git a/phpstan.neon b/phpstan.neon
new file mode 100644
index 0000000..dec5108
--- /dev/null
+++ b/phpstan.neon
@@ -0,0 +1,4 @@
+parameters:
+ paths:
+ - ./src
+ level: 5
\ No newline at end of file
diff --git a/src/Codeception/Lib/Connector/Guzzle.php b/src/Codeception/Lib/Connector/Guzzle.php
index 3816b1d..55594bd 100644
--- a/src/Codeception/Lib/Connector/Guzzle.php
+++ b/src/Codeception/Lib/Connector/Guzzle.php
@@ -23,6 +23,9 @@
class Guzzle extends AbstractBrowser
{
+ /**
+ * @var array
+ */
protected array $requestOptions = [
'allow_redirects' => false,
'headers' => [],
@@ -115,7 +118,7 @@ protected function createResponse(Psr7Response $psr7Response): BrowserKitRespons
$contentType = 'text/html';
}
- if (strpos($contentType, 'charset=') === false) {
+ if (str_contains($contentType, 'charset=') === false) {
if (preg_match('#]+charset *= *["\']?([a-zA-Z\-0-9]+)#i', $body, $matches)) {
$contentType .= ';charset=' . $matches[1];
}
@@ -159,10 +162,10 @@ protected function createResponse(Psr7Response $psr7Response): BrowserKitRespons
protected function getAbsoluteUri(string $uri): string
{
$baseUri = $this->client->getConfig('base_uri');
- if (strpos($uri, '://') === false && strpos($uri, '//') !== 0) {
- if (strpos($uri, '/') === 0) {
+ if (str_contains($uri, '://') === false && !str_starts_with($uri, '//')) {
+ if (str_starts_with($uri, '/')) {
$baseUriPath = $baseUri->getPath();
- if (!empty($baseUriPath) && strpos($uri, (string) $baseUriPath) === 0) {
+ if (!empty($baseUriPath) && str_starts_with($uri, (string) $baseUriPath)) {
$uri = substr($uri, strlen($baseUriPath));
}
@@ -178,9 +181,9 @@ protected function getAbsoluteUri(string $uri): string
return Uri::mergeUrls((string)$baseUri, $uri);
}
- protected function doRequest($request)
+ protected function doRequest(object $request)
{
- /** @var $request BrowserKitRequest **/
+ /** @var BrowserKitRequest $request **/
$guzzleRequest = new Psr7Request(
$request->getMethod(),
$request->getUri(),
@@ -190,17 +193,17 @@ protected function doRequest($request)
$options = $this->requestOptions;
$options['cookies'] = $this->extractCookies($guzzleRequest->getUri()->getHost());
$multipartData = $this->extractMultipartFormData($request);
- if (!empty($multipartData)) {
+ if ($multipartData !== []) {
$options['multipart'] = $multipartData;
}
$formData = $this->extractFormData($request);
- if (empty($multipartData) && $formData) {
+ if ($multipartData === [] && $formData) {
$options['form_params'] = $formData;
}
try {
- if (null !== $this->awsCredentials) {
+ if ($this->awsCredentials instanceof AwsCredentials) {
$response = $this->client->send($this->awsSignature->signRequest($guzzleRequest, $this->awsCredentials), $options);
} else {
$response = $this->client->send($guzzleRequest, $options);
@@ -213,6 +216,7 @@ protected function doRequest($request)
$response = $exception->getResponse();
}
+ // @phpstan-ignore-next-line
return $this->createResponse($response);
}
@@ -227,7 +231,7 @@ protected function extractHeaders(BrowserKitRequest $request): array
$contentHeaders = ['Content-Length' => true, 'Content-Md5' => true, 'Content-Type' => true];
foreach ($server as $header => $val) {
$header = html_entity_decode(implode('-', array_map('ucfirst', explode('-', strtolower(str_replace('_', '-', $header))))), ENT_NOQUOTES);
- if (strpos($header, 'Http-') === 0) {
+ if (str_starts_with($header, 'Http-')) {
$headers[substr($header, 5)] = $val;
} elseif (isset($contentHeaders[$header])) {
$headers[$header] = $val;
@@ -237,6 +241,9 @@ protected function extractHeaders(BrowserKitRequest $request): array
return $headers;
}
+ /**
+ * @return array|null
+ */
protected function extractFormData(BrowserKitRequest $browserKitRequest): ?array
{
if (!in_array(strtoupper($browserKitRequest->getMethod()), ['POST', 'PUT', 'PATCH', 'DELETE'])) {
@@ -257,14 +264,17 @@ protected function extractFormData(BrowserKitRequest $browserKitRequest): ?array
return $browserKitRequest->getParameters();
}
- protected function extractMultipartFormData(BrowserKitRequest $browserKitRequest)
+ /**
+ * @return array
+ */
+ protected function extractMultipartFormData(BrowserKitRequest $browserKitRequest): array
{
if (!in_array(strtoupper($browserKitRequest->getMethod()), ['POST', 'PUT', 'PATCH'])) {
return [];
}
$parts = $this->mapFiles($browserKitRequest->getFiles());
- if (empty($parts)) {
+ if ($parts === []) {
return [];
}
@@ -275,11 +285,14 @@ protected function extractMultipartFormData(BrowserKitRequest $browserKitRequest
return $parts;
}
- protected function formatMultipart($parts, $key, $value)
+ /**
+ * @return array
+ */
+ protected function formatMultipart(mixed $parts, string $key, mixed $value): array
{
if (is_array($value)) {
foreach ($value as $subKey => $subValue) {
- $parts = array_merge($this->formatMultipart([], $key.sprintf('[%s]', $subKey), $subValue), $parts);
+ $parts = array_merge($this->formatMultipart([], $key . sprintf('[%s]', $subKey), $subValue), $parts);
}
return $parts;
@@ -289,7 +302,11 @@ protected function formatMultipart($parts, $key, $value)
return $parts;
}
- protected function mapFiles($requestFiles, $arrayName = ''): array
+ /**
+ * @param array $requestFiles
+ * @return array
+ */
+ protected function mapFiles(array $requestFiles, ?string $arrayName = ''): array
{
$files = [];
foreach ($requestFiles as $name => $info) {
@@ -329,7 +346,7 @@ protected function mapFiles($requestFiles, $arrayName = ''): array
return $files;
}
- protected function extractCookies($host): GuzzleCookieJar
+ protected function extractCookies(string $host): GuzzleCookieJar
{
$jar = [];
$cookies = $this->getCookieJar()->all();
@@ -345,7 +362,7 @@ protected function extractCookies($host): GuzzleCookieJar
return new GuzzleCookieJar(false, $jar);
}
- public static function createHandler($handler): GuzzleHandlerStack
+ public static function createHandler(mixed $handler): GuzzleHandlerStack
{
if ($handler instanceof GuzzleHandlerStack) {
return $handler;
@@ -360,7 +377,7 @@ public static function createHandler($handler): GuzzleHandlerStack
}
if (is_string($handler) && class_exists($handler)) {
- return GuzzleHandlerStack::create(new $handler);
+ return GuzzleHandlerStack::create(new $handler());
}
if (is_callable($handler)) {
@@ -370,7 +387,10 @@ public static function createHandler($handler): GuzzleHandlerStack
return GuzzleHandlerStack::create();
}
- public function setAwsAuth($config): void
+ /**
+ * @param array $config
+ */
+ public function setAwsAuth(array $config): void
{
$this->awsCredentials = new AwsCredentials($config['key'], $config['secret']);
$this->awsSignature = new AwsSignatureV4($config['service'], $config['region']);
diff --git a/src/Codeception/Module/PhpBrowser.php b/src/Codeception/Module/PhpBrowser.php
index 6b6f90e..5049f5c 100644
--- a/src/Codeception/Module/PhpBrowser.php
+++ b/src/Codeception/Module/PhpBrowser.php
@@ -135,7 +135,7 @@ public function _initialize()
public function _before(TestInterface $test)
{
- if (!$this->client) {
+ if (!$this->client instanceof AbstractBrowser) {
$this->client = new Guzzle();
}
@@ -155,12 +155,14 @@ public function setHeader(string $name, string $value): void
$this->haveHttpHeader($name, $value);
}
- public function amHttpAuthenticated($username, $password): void
+ public function amHttpAuthenticated(string $username, string $password): void
{
- $this->client->setAuth($username, $password);
+ if ($this->client instanceof Guzzle) {
+ $this->client->setAuth($username, $password);
+ }
}
- public function amOnUrl($url): void
+ public function amOnUrl(string $url): void
{
$host = Uri::retrieveHost($url);
$config = $this->config;
@@ -175,7 +177,7 @@ public function amOnUrl($url): void
$this->amOnPage($page);
}
- public function amOnSubdomain($subdomain): void
+ public function amOnSubdomain(string $subdomain): void
{
$url = $this->config['url'];
$url = preg_replace('#(https?://)(.*\.)(.*\.)#', "$1$3", $url); // removing current subdomain
@@ -206,18 +208,13 @@ protected function onReconfigure()
*
* It is not recommended to use this command on a regular basis.
* If Codeception lacks important Guzzle Client methods, implement them and submit patches.
- *
- * @return mixed
*/
- public function executeInGuzzle(Closure $function)
+ public function executeInGuzzle(Closure $function): mixed
{
return $function($this->guzzle);
}
- /**
- * @return int|string
- */
- public function _getResponseCode()
+ public function _getResponseCode(): int|string
{
return $this->getResponseStatusCode();
}
@@ -245,21 +242,24 @@ public function _prepareSession(): void
$defaults['base_uri'] = $this->config['url'];
$defaults['curl'] = $curlOptions;
- $handler = Guzzle::createHandler($this->config['handler']);
- if ($handler && is_array($this->config['middleware'])) {
+ $handlerStack = Guzzle::createHandler($this->config['handler']);
+ if (is_array($this->config['middleware'])) {
foreach ($this->config['middleware'] as $middleware) {
- $handler->push($middleware);
+ $handlerStack->push($middleware);
}
}
- $defaults['handler'] = $handler;
+ $defaults['handler'] = $handlerStack;
$this->guzzle = new GuzzleClient($defaults);
$this->client->setRefreshMaxInterval($this->config['refresh_max_interval']);
$this->client->setClient($this->guzzle);
}
- public function _backupSession(): array
+ /**
+ * @return array
+ */
+ public function _backupSession()
{
return [
'client' => $this->client,
@@ -269,6 +269,9 @@ public function _backupSession(): array
];
}
+ /**
+ * @param array $session
+ */
public function _loadSession($session): void
{
foreach ($session as $key => $val) {
@@ -276,6 +279,9 @@ public function _loadSession($session): void
}
}
+ /**
+ * @param ?array $session
+ */
public function _closeSession($session = null): void
{
unset($session);
diff --git a/tests/_support/UnitTester.php b/tests/_support/UnitTester.php
index e19544a..6aeac82 100644
--- a/tests/_support/UnitTester.php
+++ b/tests/_support/UnitTester.php
@@ -1,6 +1,5 @@
GET();
}
}
-class contentType1 {
- function GET() {
+class contentType1
+{
+ function GET()
+ {
header('Content-Type:', true);
- include __DIR__.'/view/content_type.php';
+ include __DIR__ . '/view/content_type.php';
}
}
-class contentType2 {
- function GET() {
+class contentType2
+{
+ function GET()
+ {
header('Content-Type:', true);
- include __DIR__.'/view/content_type2.php';
+ include __DIR__ . '/view/content_type2.php';
}
}
-class unsetCookie {
- function GET() {
+class unsetCookie
+{
+ function GET()
+ {
header('Set-Cookie: a=; Expires=Thu, 01 Jan 1970 00:00:01 GMT');
}
}
-class basehref {
- function GET() {
- include __DIR__.'/view/basehref.php';
+class basehref
+{
+ function GET()
+ {
+ include __DIR__ . '/view/basehref.php';
}
}
-class jserroronload {
- function GET() {
- include __DIR__.'/view/jserroronload.php';
+class jserroronload
+{
+ function GET()
+ {
+ include __DIR__ . '/view/jserroronload.php';
}
}
-class userAgent {
- function GET() {
+class userAgent
+{
+ function GET()
+ {
echo $_SERVER['HTTP_USER_AGENT'];
}
}
-class minimal {
- function GET() {
- include __DIR__.'/view/minimal.php';
+class minimal
+{
+ function GET()
+ {
+ include __DIR__ . '/view/minimal.php';
}
}
diff --git a/tests/data/app/data.php b/tests/data/app/data.php
index c1877c2..d7dca0e 100755
--- a/tests/data/app/data.php
+++ b/tests/data/app/data.php
@@ -1,9 +1,11 @@
$class) {
- $regex = str_replace('/', '\/', $regex);
- $regex = '^' . $regex . '\/?$';
- if (preg_match("/$regex/i", $path, $matches)) {
- $found = true;
- if (class_exists($class)) {
- $obj = new $class;
- if (method_exists($obj, $method)) {
- $obj->$method($matches);
- } else {
- throw new BadMethodCallException("Method, $method, not supported.");
- }
+ foreach ($urls as $regex => $class) {
+ $regex = str_replace('/', '\/', $regex);
+ $regex = '^' . $regex . '\/?$';
+ if (preg_match("/$regex/i", $path, $matches)) {
+ $found = true;
+ if (class_exists($class)) {
+ $obj = new $class();
+ if (method_exists($obj, $method)) {
+ $obj->$method($matches);
} else {
- throw new Exception("Class, $class, not found.");
+ throw new BadMethodCallException("Method, $method, not supported.");
}
- break;
+ } else {
+ throw new Exception("Class, $class, not found.");
}
+ break;
}
- if (!$found) {
- throw new Exception("URL, $path, not found.");
- }
+ }
+ if (!$found) {
+ throw new Exception("URL, $path, not found.");
}
}
+}
diff --git a/tests/data/app/index.php b/tests/data/app/index.php
index 4c65804..243d816 100644
--- a/tests/data/app/index.php
+++ b/tests/data/app/index.php
@@ -1,10 +1,12 @@
'index',
'/info' => 'info',
@@ -42,5 +44,4 @@
'/jserroronload' => 'jserroronload',
'/minimal' => 'minimal',
);
-
glue::stick($urls);
diff --git a/tests/data/app/view/form/bug3824.php b/tests/data/app/view/form/bug3824.php
index 221fb98..64a02ff 100644
--- a/tests/data/app/view/form/bug3824.php
+++ b/tests/data/app/view/form/bug3824.php
@@ -7,9 +7,9 @@