Skip to content
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

Improve type inference for public method return types #59

Merged
merged 1 commit into from
Dec 17, 2024
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
8 changes: 4 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@
"ext-mbstring": "*"
},
"require-dev": {
"infection/infection": "^0.27.9",
"laminas/laminas-coding-standard": "~3.0.0",
"infection/infection": "^0.27.11",
"laminas/laminas-coding-standard": "~3.0.1",
"maglnet/composer-require-checker": "^3.8.0",
"phpunit/phpunit": "^9.6.16",
"phpunit/phpunit": "^9.6.22",
"psalm/plugin-phpunit": "^0.19.0",
"vimeo/psalm": "^5.21.1"
"vimeo/psalm": "^5.26.1"
},
"autoload": {
"psr-4": {
Expand Down
14 changes: 7 additions & 7 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion psalm-baseline.xml
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="5.9.0@8b9ad1eb9e8b7d3101f949291da2b9f7767cd163"/>
<files psalm-version="5.26.1@d747f6500b38ac4f7dfc5edbcae6e4b637d7add0">
<file src="src/Escaper.php">
<TypeDoesNotContainType>
<code><![CDATA[$encoding === '']]></code>
</TypeDoesNotContainType>
</file>
</files>
19 changes: 11 additions & 8 deletions src/Escaper.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

/**
* Context specific methods for use in secure output escaping
*
* @final
*/
class Escaper
{
Expand All @@ -49,7 +51,7 @@
* Current encoding for escaping. If not UTF-8, we convert strings from this encoding
* pre-escaping and back to this encoding post-escaping.
*
* @var string
* @var non-empty-string
*/
protected $encoding = 'utf-8';

Expand Down Expand Up @@ -88,7 +90,7 @@
/**
* List of all encoding supported by this class
*
* @var array
* @var list<non-empty-string>
*/
protected $supportedEncodings = [
'iso-8859-1',
Expand Down Expand Up @@ -131,21 +133,22 @@
* Constructor: Single parameter allows setting of global encoding for use by
* the current object.
*
* @param non-empty-string|null $encoding
* @throws Exception\InvalidArgumentException
*/
public function __construct(?string $encoding = null)
{
if ($encoding !== null) {
if ($encoding === '') {
throw new Exception\InvalidArgumentException(

Check warning on line 143 in src/Escaper.php

View workflow job for this annotation

GitHub Actions / ci / QA Checks (Infection [8.1, locked], ubuntu-latest, laminas/laminas-continuous-integration-action@...

Escaped Mutant for Mutator "Throw_": --- Original +++ New @@ @@ { if ($encoding !== null) { if ($encoding === '') { - throw new Exception\InvalidArgumentException(static::class . ' constructor parameter does not allow a blank value'); + new Exception\InvalidArgumentException(static::class . ' constructor parameter does not allow a blank value'); } $encoding = strtolower($encoding); if (!in_array($encoding, $this->supportedEncodings)) {
static::class . ' constructor parameter does not allow a blank value'

Check warning on line 144 in src/Escaper.php

View workflow job for this annotation

GitHub Actions / ci / QA Checks (Infection [8.1, locked], ubuntu-latest, laminas/laminas-continuous-integration-action@...

Escaped Mutant for Mutator "Concat": --- Original +++ New @@ @@ { if ($encoding !== null) { if ($encoding === '') { - throw new Exception\InvalidArgumentException(static::class . ' constructor parameter does not allow a blank value'); + throw new Exception\InvalidArgumentException(' constructor parameter does not allow a blank value' . static::class); } $encoding = strtolower($encoding); if (!in_array($encoding, $this->supportedEncodings)) {

Check warning on line 144 in src/Escaper.php

View workflow job for this annotation

GitHub Actions / ci / QA Checks (Infection [8.1, locked], ubuntu-latest, laminas/laminas-continuous-integration-action@...

Escaped Mutant for Mutator "ConcatOperandRemoval": --- Original +++ New @@ @@ { if ($encoding !== null) { if ($encoding === '') { - throw new Exception\InvalidArgumentException(static::class . ' constructor parameter does not allow a blank value'); + throw new Exception\InvalidArgumentException(' constructor parameter does not allow a blank value'); } $encoding = strtolower($encoding); if (!in_array($encoding, $this->supportedEncodings)) {

Check warning on line 144 in src/Escaper.php

View workflow job for this annotation

GitHub Actions / ci / QA Checks (Infection [8.1, locked], ubuntu-latest, laminas/laminas-continuous-integration-action@...

Escaped Mutant for Mutator "ConcatOperandRemoval": --- Original +++ New @@ @@ { if ($encoding !== null) { if ($encoding === '') { - throw new Exception\InvalidArgumentException(static::class . ' constructor parameter does not allow a blank value'); + throw new Exception\InvalidArgumentException(static::class); } $encoding = strtolower($encoding); if (!in_array($encoding, $this->supportedEncodings)) {
);
}

$encoding = strtolower($encoding);
if (! in_array($encoding, $this->supportedEncodings)) {
throw new Exception\InvalidArgumentException(
'Value of \'' . $encoding . '\' passed to ' . static::class

Check warning on line 151 in src/Escaper.php

View workflow job for this annotation

GitHub Actions / ci / QA Checks (Infection [8.1, locked], ubuntu-latest, laminas/laminas-continuous-integration-action@...

Escaped Mutant for Mutator "Concat": --- Original +++ New @@ @@ } $encoding = strtolower($encoding); if (!in_array($encoding, $this->supportedEncodings)) { - throw new Exception\InvalidArgumentException('Value of \'' . $encoding . '\' passed to ' . static::class . ' constructor parameter is invalid. Provide an encoding supported by htmlspecialchars()'); + throw new Exception\InvalidArgumentException($encoding . 'Value of \'' . '\' passed to ' . static::class . ' constructor parameter is invalid. Provide an encoding supported by htmlspecialchars()'); } $this->encoding = $encoding; }

Check warning on line 151 in src/Escaper.php

View workflow job for this annotation

GitHub Actions / ci / QA Checks (Infection [8.1, locked], ubuntu-latest, laminas/laminas-continuous-integration-action@...

Escaped Mutant for Mutator "ConcatOperandRemoval": --- Original +++ New @@ @@ } $encoding = strtolower($encoding); if (!in_array($encoding, $this->supportedEncodings)) { - throw new Exception\InvalidArgumentException('Value of \'' . $encoding . '\' passed to ' . static::class . ' constructor parameter is invalid. Provide an encoding supported by htmlspecialchars()'); + throw new Exception\InvalidArgumentException($encoding . '\' passed to ' . static::class . ' constructor parameter is invalid. Provide an encoding supported by htmlspecialchars()'); } $this->encoding = $encoding; }

Check warning on line 151 in src/Escaper.php

View workflow job for this annotation

GitHub Actions / ci / QA Checks (Infection [8.1, locked], ubuntu-latest, laminas/laminas-continuous-integration-action@...

Escaped Mutant for Mutator "ConcatOperandRemoval": --- Original +++ New @@ @@ } $encoding = strtolower($encoding); if (!in_array($encoding, $this->supportedEncodings)) { - throw new Exception\InvalidArgumentException('Value of \'' . $encoding . '\' passed to ' . static::class . ' constructor parameter is invalid. Provide an encoding supported by htmlspecialchars()'); + throw new Exception\InvalidArgumentException('Value of \'' . '\' passed to ' . static::class . ' constructor parameter is invalid. Provide an encoding supported by htmlspecialchars()'); } $this->encoding = $encoding; }

Check warning on line 151 in src/Escaper.php

View workflow job for this annotation

GitHub Actions / ci / QA Checks (Infection [8.1, locked], ubuntu-latest, laminas/laminas-continuous-integration-action@...

Escaped Mutant for Mutator "Concat": --- Original +++ New @@ @@ } $encoding = strtolower($encoding); if (!in_array($encoding, $this->supportedEncodings)) { - throw new Exception\InvalidArgumentException('Value of \'' . $encoding . '\' passed to ' . static::class . ' constructor parameter is invalid. Provide an encoding supported by htmlspecialchars()'); + throw new Exception\InvalidArgumentException('Value of \'' . '\' passed to ' . $encoding . static::class . ' constructor parameter is invalid. Provide an encoding supported by htmlspecialchars()'); } $this->encoding = $encoding; }

Check warning on line 151 in src/Escaper.php

View workflow job for this annotation

GitHub Actions / ci / QA Checks (Infection [8.1, locked], ubuntu-latest, laminas/laminas-continuous-integration-action@...

Escaped Mutant for Mutator "ConcatOperandRemoval": --- Original +++ New @@ @@ } $encoding = strtolower($encoding); if (!in_array($encoding, $this->supportedEncodings)) { - throw new Exception\InvalidArgumentException('Value of \'' . $encoding . '\' passed to ' . static::class . ' constructor parameter is invalid. Provide an encoding supported by htmlspecialchars()'); + throw new Exception\InvalidArgumentException('Value of \'' . $encoding . static::class . ' constructor parameter is invalid. Provide an encoding supported by htmlspecialchars()'); } $this->encoding = $encoding; }

Check warning on line 151 in src/Escaper.php

View workflow job for this annotation

GitHub Actions / ci / QA Checks (Infection [8.1, locked], ubuntu-latest, laminas/laminas-continuous-integration-action@...

Escaped Mutant for Mutator "Concat": --- Original +++ New @@ @@ } $encoding = strtolower($encoding); if (!in_array($encoding, $this->supportedEncodings)) { - throw new Exception\InvalidArgumentException('Value of \'' . $encoding . '\' passed to ' . static::class . ' constructor parameter is invalid. Provide an encoding supported by htmlspecialchars()'); + throw new Exception\InvalidArgumentException('Value of \'' . $encoding . static::class . '\' passed to ' . ' constructor parameter is invalid. Provide an encoding supported by htmlspecialchars()'); } $this->encoding = $encoding; }
. ' constructor parameter is invalid. Provide an encoding supported by htmlspecialchars()'
);
}
Expand All @@ -171,7 +174,7 @@
/**
* Return the encoding that all output/input is expected to be encoded in.
*
* @return string
* @return non-empty-string
*/
public function getEncoding()
{
Expand All @@ -182,7 +185,7 @@
* Escape a string for the HTML Body context where there are very few characters
* of special meaning. Internally this will use htmlspecialchars().
*
* @return string
* @return ($string is non-empty-string ? non-empty-string : string)
*/
public function escapeHtml(string $string)
{
Expand All @@ -194,7 +197,7 @@
* to escape that are not covered by htmlspecialchars() to cover cases where an attribute
* might be unquoted or quoted illegally (e.g. backticks are valid quotes for IE).
*
* @return string
* @return ($string is non-empty-string ? non-empty-string : string)
*/
public function escapeHtmlAttr(string $string)
{
Expand All @@ -216,7 +219,7 @@
* Backslash escaping is not used as it still leaves the escaped character as-is and so
* is not useful in a HTML context.
*
* @return string
* @return ($string is non-empty-string ? non-empty-string : string)
*/
public function escapeJs(string $string)
{
Expand All @@ -234,7 +237,7 @@
* an entire URI - only a subcomponent being inserted. The function is a simple proxy
* to rawurlencode() which now implements RFC 3986 since PHP 5.3 completely.
*
* @return string
* @return ($string is non-empty-string ? non-empty-string : string)
*/
public function escapeUrl(string $string)
{
Expand All @@ -245,7 +248,7 @@
* Escape a string for the CSS context. CSS escaping can be applied to any string being
* inserted into CSS and escapes everything except alphanumerics.
*
* @return string
* @return ($string is non-empty-string ? non-empty-string : string)
*/
public function escapeCss(string $string)
{
Expand Down
4 changes: 3 additions & 1 deletion test/EscaperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ protected function setUp(): void
public function testSettingEncodingToEmptyStringShouldThrowException(): void
{
$this->expectException(InvalidArgumentException::class);
/** @psalm-suppress InvalidArgument */
new Escaper('');
}

/** @return array<array-key, array{0: string}> */
/** @return array<array-key, array{0: non-empty-string}> */
public function supportedEncodingsProvider(): array
{
return [
Expand Down Expand Up @@ -70,6 +71,7 @@ public function supportedEncodingsProvider(): array
}

/**
* @param non-empty-string $encoding
* @dataProvider supportedEncodingsProvider
*/
public function testSettingValidEncodingShouldNotThrowExceptions(string $encoding): void
Expand Down
59 changes: 59 additions & 0 deletions test/StaticAnalysis/EscaperReturnTypeChecks.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

declare(strict_types=1);

namespace LaminasTest\Escaper\StaticAnalysis;

use Laminas\Escaper\Escaper;

/** @psalm-suppress UnusedClass */
final class EscaperReturnTypeChecks
{
public function __construct(private readonly Escaper $escaper)
{
}

/** @return non-empty-string */
public function escapeHtmlReturnsNonEmptyString(): string
{
return $this->escaper->escapeHtml('Not Empty');
}

public function escapeHtmlReturnsEmptyString(): string
{
return $this->escaper->escapeHtml('');
}

/** @return non-empty-string */
public function escapeJsReturnsNonEmptyString(): string
{
return $this->escaper->escapeJs('Not Empty');
}

public function escapeJsReturnsEmptyString(): string
{
return $this->escaper->escapeJs('');
}

/** @return non-empty-string */
public function escapeCssReturnsNonEmptyString(): string
{
return $this->escaper->escapeCss('Not Empty');
}

public function escapeCssReturnsEmptyString(): string
{
return $this->escaper->escapeCss('');
}

/** @return non-empty-string */
public function escapeAttributeReturnsNonEmptyString(): string
{
return $this->escaper->escapeHtmlAttr('Not Empty');
}

public function escapeAttributeReturnsEmptyString(): string
{
return $this->escaper->escapeHtmlAttr('');
}
}
Loading