Skip to content
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
2 changes: 1 addition & 1 deletion .github/workflows/test-old.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
uses: 'shivammathur/setup-php@v2'
with:
php-version: '${{ matrix.php }}'
tools: 'composer:v1'
tools: 'composer:v2'
coverage: 'xdebug'
- name: 'PHP'
run: 'php -v'
Expand Down
7 changes: 3 additions & 4 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,11 @@ jobs:
runs-on: '${{ matrix.os }}'
strategy:
matrix:
php: ['7.4', '8.0', '8.1', '8.2', '8.3']
php: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5']
os: ['ubuntu-latest']
failure: [false]
include:
- { php: '8.4', os: 'ubuntu-latest', failure: true } # Psalm does not support PHP 8.4 yet
- { php: '8.5', os: 'ubuntu-latest', failure: true } # '8.5' means 'nightly'
- { php: '8.6', os: 'ubuntu-latest', failure: true } # '8.6' means 'nightly'
steps:
- name: 'Checkout'
uses: 'actions/checkout@v4'
Expand All @@ -38,7 +37,7 @@ jobs:
- name: 'Psalm'
run: |
composer remove --dev -W 'phpunit/phpunit'
composer require --dev -W 'vimeo/psalm=^5.0' 'nikic/php-parser=^4.0'
composer require --dev -W 'vimeo/psalm=>=5.0' 'nikic/php-parser=>=4.0'
php vendor/bin/psalm --shepherd --php-version=${{ matrix.php }}
continue-on-error: '${{ matrix.failure }}'
- name: 'Infection'
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
PHP_VERSION ?= 8.0
PHP_VERSION ?= 8.5
PHP := docker-compose run --rm php-${PHP_VERSION}

php-version:
Expand Down
5 changes: 5 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,8 @@ services:
php-7.3: { <<: *php, build: { context: docker/php, args: { PHP_VERSION: 7.3 } } }
php-7.4: { <<: *php, build: { context: docker/php, args: { PHP_VERSION: 7.4 } } }
php-8.0: { <<: *php, build: { context: docker/php, args: { PHP_VERSION: 8.0 } } }
php-8.1: { <<: *php, build: { context: docker/php, args: { PHP_VERSION: 8.1 } } }
php-8.2: { <<: *php, build: { context: docker/php, args: { PHP_VERSION: 8.2 } } }
php-8.3: { <<: *php, build: { context: docker/php, args: { PHP_VERSION: 8.3 } } }
php-8.4: { <<: *php, build: { context: docker/php, args: { PHP_VERSION: 8.4 } } }
php-8.5: { <<: *php, build: { context: docker/php, args: { PHP_VERSION: 8.5 } } }
2 changes: 1 addition & 1 deletion docker/php/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ ARG PHP_VERSION=8.0
FROM php:$PHP_VERSION

RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
&& php -r "if (hash_file('sha384', 'composer-setup.php') === '906a84df04cea2aa72f40b5f787e49f22d4c2f19492ac310e8cba5b96ac8b64115ac402c8cd292b8a03482574915d1a8') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" \
&& php -r "if (hash_file('sha384', 'composer-setup.php') === 'c8b085408188070d5f52bcfe4ecfbee5f727afa458b2573b8eaaf77b3419b0bf2768dc67c86944da1544f06fa544fd47') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" \
&& php composer-setup.php \
&& php -r "unlink('composer-setup.php');" \
&& mv composer.phar /usr/local/bin/composer
Expand Down
5 changes: 5 additions & 0 deletions psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,9 @@
</ignoreFiles>
</projectFiles>

<issueHandlers>
<MissingOverrideAttribute errorLevel="suppress" />
<UnusedPsalmSuppress errorLevel="suppress" />
</issueHandlers>

</psalm>
1 change: 1 addition & 0 deletions src/Event/FilterShortcodesEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
* is used directly in processor.
*
* @author Tomasz Kowalczyk <[email protected]>
* @psalm-suppress ClassMustBeFinal
*/
class FilterShortcodesEvent
{
Expand All @@ -24,7 +25,7 @@
*/
public function __construct(array $shortcodes, $parent = null)
{
if(null !== $parent && false === $parent instanceof ProcessedShortcode) {

Check warning on line 28 in src/Event/FilterShortcodesEvent.php

View workflow job for this annotation

GitHub Actions / test (8.0, ubuntu-latest, false)

Escaped Mutant for Mutator "InstanceOf_": --- Original +++ New @@ @@ */ public function __construct(array $shortcodes, $parent = null) { - if (null !== $parent && false === $parent instanceof ProcessedShortcode) { + if (null !== $parent && false === true) { throw new \LogicException('Parameter $parent must be an instance of ProcessedShortcode.'); } $this->parent = $parent;

Check warning on line 28 in src/Event/FilterShortcodesEvent.php

View workflow job for this annotation

GitHub Actions / test (8.1, ubuntu-latest, false)

Escaped Mutant for Mutator "InstanceOf_": @@ @@ */ public function __construct(array $shortcodes, $parent = null) { - if (null !== $parent && false === $parent instanceof ProcessedShortcode) { + if (null !== $parent && false === true) { throw new \LogicException('Parameter $parent must be an instance of ProcessedShortcode.'); } $this->parent = $parent;
throw new \LogicException('Parameter $parent must be an instance of ProcessedShortcode.');
}

Expand Down
1 change: 1 addition & 0 deletions src/Event/ReplaceShortcodesEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
* results in the source text.
*
* @author Tomasz Kowalczyk <[email protected]>
* @psalm-suppress ClassMustBeFinal
*/
class ReplaceShortcodesEvent
{
Expand All @@ -29,7 +30,7 @@
*/
public function __construct($text, array $replacements, $shortcode = null)
{
if(null !== $shortcode && false === $shortcode instanceof ShortcodeInterface) {

Check warning on line 33 in src/Event/ReplaceShortcodesEvent.php

View workflow job for this annotation

GitHub Actions / test (8.0, ubuntu-latest, false)

Escaped Mutant for Mutator "InstanceOf_": --- Original +++ New @@ @@ */ public function __construct($text, array $replacements, $shortcode = null) { - if (null !== $shortcode && false === $shortcode instanceof ShortcodeInterface) { + if (null !== $shortcode && false === true) { throw new \LogicException('Parameter $shortcode must be an instance of ShortcodeInterface.'); } $this->shortcode = $shortcode;

Check warning on line 33 in src/Event/ReplaceShortcodesEvent.php

View workflow job for this annotation

GitHub Actions / test (8.1, ubuntu-latest, false)

Escaped Mutant for Mutator "InstanceOf_": @@ @@ */ public function __construct($text, array $replacements, $shortcode = null) { - if (null !== $shortcode && false === $shortcode instanceof ShortcodeInterface) { + if (null !== $shortcode && false === true) { throw new \LogicException('Parameter $shortcode must be an instance of ShortcodeInterface.'); } $this->shortcode = $shortcode;
throw new \LogicException('Parameter $shortcode must be an instance of ShortcodeInterface.');
}

Expand Down
3 changes: 3 additions & 0 deletions src/Parser/RegexParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public function parse($text)
// loop instead of array_map to pass the arguments explicitly
$shortcodes = array();
foreach($matches[0] as $match) {
/** @psalm-suppress PossiblyFalseArgument */
$offset = mb_strlen(substr($text, 0, $match[1]), 'utf-8');
$shortcodes[] = $this->parseSingle($match[0], $offset);
}
Expand Down Expand Up @@ -108,11 +109,13 @@ private function parseValue($value)
* @param string $value
*
* @return string
* @psalm-suppress InvalidFalsableReturnType
*/
private function extractValue($value)
{
$length = strlen($this->syntax->getParameterValueDelimiter());

/** @psalm-suppress FalsableReturnStatement */
return $this->isDelimitedValue($value) ? substr($value, $length, -1 * $length) : $value;
}

Expand Down
17 changes: 15 additions & 2 deletions src/Parser/RegularParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

/**
* @author Tomasz Kowalczyk <[email protected]>
* @psalm-suppress PossiblyUndefinedArrayOffset
* @psalm-suppress PossiblyUndefinedVariable
*/
final class RegularParser implements ParserInterface
{
Expand Down Expand Up @@ -79,6 +81,7 @@ public function parse($text)
}
}
}
/** @psalm-suppress PossiblyFalseArgument */
ini_set('xdebug.max_nesting_level', $nestingLevel);

return $shortcodes;
Expand Down Expand Up @@ -269,7 +272,9 @@ private function getBacktrack()
{
$position = array_pop($this->backtracks);
$backtrack = '';
/** @psalm-suppress PossiblyNullOperand */
for($i = $position; $i < $this->position; $i++) {
/** @psalm-suppress PossiblyNullArrayOffset */
$backtrack .= $this->tokens[$i][1];
}

Expand All @@ -285,13 +290,17 @@ private function backtrack($modifyPosition = true)
{
$position = array_pop($this->backtracks);
if($modifyPosition) {
/** @psalm-suppress PossiblyNullPropertyAssignmentValue */
$this->position = $position;
}

$backtrack = '';
/** @psalm-suppress PossiblyNullOperand */
for($i = $position; $i < $this->lastBacktrack; $i++) {
/** @psalm-suppress PossiblyNullArrayOffset */
$backtrack .= $this->tokens[$i][1];
}
/** @psalm-suppress PossiblyNullPropertyAssignmentValue */
$this->lastBacktrack = $position;

return $backtrack;
Expand Down Expand Up @@ -339,6 +348,7 @@ private function match($type, $ws)
* @param string $text
*
* @psalm-return list<array{0:int,1:string,2:int}>
* @psalm-suppress MixedReturnTypeCoercion
*/
private function tokenize($text)
{
Expand All @@ -362,21 +372,23 @@ private function tokenize($text)
default: { throw new \RuntimeException('Invalid token.'); }
}
$tokens[] = array($type, $token, $position);
/** @psalm-suppress MixedArgument */
$position += mb_strlen($token, 'utf-8');
}

/** @psalm-suppress MixedReturnTypeCoercion */
return $tokens;
}

/** @return non-empty-string */
private function prepareLexer(SyntaxInterface $syntax)
{
// FIXME: for some reason Psalm does not understand the `@psalm-var callable() $var` annotation
/** @psalm-suppress MissingClosureParamType, MissingClosureReturnType */
/** @psalm-suppress MissingClosureParamType,MissingClosureReturnType,PossiblyNullOperand */
$group = function($text, $group) {
return '(?<'.(string)$group.'>'.preg_replace('/(.)/us', '\\\\$0', (string)$text).')';
};
/** @psalm-suppress MissingClosureParamType, MissingClosureReturnType */
/** @psalm-suppress MissingClosureParamType,MissingClosureReturnType */
$quote = function($text) {
return preg_replace('/(.)/us', '\\\\$0', (string)$text);
};
Expand All @@ -388,6 +400,7 @@ private function prepareLexer(SyntaxInterface $syntax)
$quote($syntax->getClosingTagMarker()),
$quote($syntax->getParameterValueSeparator()),
$quote($syntax->getParameterValueDelimiter()),
'\\\\',
'\s+',
)).').)+)',
'(?<ws>\s+)',
Expand Down
2 changes: 2 additions & 0 deletions src/Parser/WordpressParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
* @see https://core.trac.wordpress.org/browser/tags/4.3.1/src/wp-includes/shortcodes.php#L239
* @see https://core.trac.wordpress.org/browser/tags/4.3.1/src/wp-includes/shortcodes.php#L448
* @psalm-suppress RiskyTruthyFalsyComparison
* @psalm-suppress PossiblyNullArgument
* @psalm-suppress PossiblyFalseArgument
*
* @author Tomasz Kowalczyk <[email protected]>
*/
Expand Down
2 changes: 2 additions & 0 deletions src/Serializer/JsonSerializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

/**
* @author Tomasz Kowalczyk <[email protected]>
* @psalm-suppress FalsableReturnStatement
* @psalm-suppress InvalidFalsableReturnType
*/
final class JsonSerializer implements SerializerInterface
{
Expand Down
3 changes: 3 additions & 0 deletions src/Serializer/YamlSerializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

/**
* @author Tomasz Kowalczyk <[email protected]>
* @psalm-suppress ReservedWord
* @psalm-suppress InvalidReturnStatement
* @psalm-suppress InvalidReturnType
*/
final class YamlSerializer implements SerializerInterface
{
Expand Down
1 change: 1 addition & 0 deletions src/ShortcodeFacade.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

/**
* @author Tomasz Kowalczyk <[email protected]>
* @psalm-suppress ClassMustBeFinal
*/
class ShortcodeFacade
{
Expand Down
22 changes: 22 additions & 0 deletions tests/ParserTest.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php
namespace Thunder\Shortcode\Tests;

use PHPUnit\Framework\Attributes\DataProvider;
use Thunder\Shortcode\HandlerContainer\HandlerContainer;
use Thunder\Shortcode\Parser\RegularParser;
use Thunder\Shortcode\Parser\ParserInterface;
Expand All @@ -23,6 +24,7 @@ final class ParserTest extends AbstractTestCase
*
* @dataProvider provideShortcodes
*/
#[DataProvider('provideShortcodes')]
public function testParser(ParserInterface $parser, $code, array $expected)
{
$this->assertShortcodes($parser->parse($code), $expected);
Expand Down Expand Up @@ -276,6 +278,26 @@ public function testIssue77()
));
}

public function testIssue119()
{
$cases = array(
'[a k="\"y"]inner[/a]' => new ParsedShortcode(new Shortcode('a', array('k' => '\"y'), 'inner', null), '[a k="\"y"]inner[/a]', 0),
'[a k=" \"y"]inner[/a]' => new ParsedShortcode(new Shortcode('a', array('k' => ' \"y'), 'inner', null), '[a k=" \"y"]inner[/a]', 0),
'[a k=" x\"y"]inner[/a]' => new ParsedShortcode(new Shortcode('a', array('k' => ' x\"y'), 'inner', null), '[a k=" x\"y"]inner[/a]', 0),
'[a k="x\"y"]inner[/a]' => new ParsedShortcode(new Shortcode('a', array('k' => 'x\"y'), 'inner', null), '[a k="x\"y"]inner[/a]', 0),
'[mention id=1 name="foo\"ff\""][/mention]' => new ParsedShortcode(new Shortcode('mention', array('id' => '1', 'name' => 'foo\"ff\"'), '', null), '[mention id=1 name="foo\"ff\""][/mention]', 0),
);
$parser = new RegularParser();
foreach($cases as $input => $expected) {
$this->assertShortcodes($parser->parse($input), array($expected));
}

$this->assertShortcodes($parser->parse('[a k="x\"y"]inner[/a] [mention id=1 name="foo\"ff\""][/mention]'), array(
new ParsedShortcode(new Shortcode('a', array('k' => 'x\"y'), 'inner', null), '[a k="x\"y"]inner[/a]', 0),
new ParsedShortcode(new Shortcode('mention', array('id' => '1', 'name' => 'foo\"ff\"'), '', null), '[mention id=1 name="foo\"ff\""][/mention]', 22),
));
}

public function testWordPress()
{
$parser = new WordpressParser();
Expand Down
3 changes: 3 additions & 0 deletions tests/ProcessorTest.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php
namespace Thunder\Shortcode\Tests;

use PHPUnit\Framework\Attributes\DataProvider;
use Thunder\Shortcode\Handler\ContentHandler;
use Thunder\Shortcode\Handler\DeclareHandler;
use Thunder\Shortcode\Handler\EmailHandler;
Expand Down Expand Up @@ -62,6 +63,7 @@ public function testReplaceWithoutContentOffset()
*
* @dataProvider provideTexts
*/
#[DataProvider('provideTexts')]
public function testProcessorProcess($text, $result)
{
$processor = new Processor(new RegexParser(), $this->getHandlers());
Expand Down Expand Up @@ -164,6 +166,7 @@ public function testProcessorShortcodePositions()
/**
* @dataProvider provideBuiltInTests
*/
#[DataProvider('provideBuiltInTests')]
public function testBuiltInHandlers($text, $result)
{
$handlers = new HandlerContainer();
Expand Down
4 changes: 4 additions & 0 deletions tests/SerializerTest.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php
namespace Thunder\Shortcode\Tests;

use PHPUnit\Framework\Attributes\DataProvider;
use Thunder\Shortcode\Serializer\JsonSerializer;
use Thunder\Shortcode\Serializer\SerializerInterface;
use Thunder\Shortcode\Serializer\TextSerializer;
Expand All @@ -18,6 +19,7 @@ final class SerializerTest extends AbstractTestCase
/**
* @dataProvider provideShortcodes
*/
#[DataProvider('provideShortcodes')]
public function testSerializer(SerializerInterface $serializer, ShortcodeInterface $test)
{
$result = $serializer->serialize($test);
Expand Down Expand Up @@ -61,6 +63,7 @@ public static function provideShortcodes()
/**
* @dataProvider provideUnserialized
*/
#[DataProvider('provideUnserialized')]
public function testUnserialize(SerializerInterface $serializer, ShortcodeInterface $test, $text)
{
$tested = $serializer->unserialize($text);
Expand Down Expand Up @@ -89,6 +92,7 @@ public static function provideUnserialized()
/**
* @dataProvider provideExceptions
*/
#[DataProvider('provideExceptions')]
public function testSerializerExceptions(SerializerInterface $serializer, $value, $exceptionClass)
{
$this->willThrowException($exceptionClass);
Expand Down
2 changes: 2 additions & 0 deletions tests/ShortcodeTest.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php
namespace Thunder\Shortcode\Tests;

use PHPUnit\Framework\Attributes\DataProvider;
use Thunder\Shortcode\HandlerContainer\HandlerContainer;
use Thunder\Shortcode\Parser\RegexParser;
use Thunder\Shortcode\Processor\Processor;
Expand All @@ -18,6 +19,7 @@ final class ShortcodeTest extends AbstractTestCase
/**
* @dataProvider provideShortcodes
*/
#[DataProvider('provideShortcodes')]
public function testShortcode($expected, $name, array $args, $content)
{
$s = new Shortcode($name, $args, $content);
Expand Down
2 changes: 2 additions & 0 deletions tests/SyntaxTest.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php
namespace Thunder\Shortcode\Tests;

use PHPUnit\Framework\Attributes\DataProvider;
use Thunder\Shortcode\Syntax\Syntax;
use Thunder\Shortcode\Syntax\CommonSyntax;
use Thunder\Shortcode\Syntax\SyntaxBuilder;
Expand All @@ -14,6 +15,7 @@ final class SyntaxTest extends AbstractTestCase
/**
* @dataProvider provideSyntaxes
*/
#[DataProvider('provideSyntaxes')]
public function testSyntax(SyntaxInterface $syntax, $open, $close, $slash, $parameter, $value)
{
static::assertSame($open, $syntax->getOpeningTag());
Expand Down
Loading