Skip to content

Commit

Permalink
feat(deps): support PHP to 8.4 and Symfony 5+
Browse files Browse the repository at this point in the history
- Upgrade minimum PHP version to 8.2 and Symfony framework to version 5+
- Remove Safe PHP library dependency due to maintenance issues and 8.4 deprecations
- Implement custom safe PHP function wrappers for internal usage
- Update testing strategy to use PHP 8.4 as default, while maintaining compatibility tests for minimum supported versions

BREAKING CHANGE: Drops support for PHP 8.1
  • Loading branch information
tigitz committed Jan 9, 2025
1 parent dfba2a6 commit bdfd1fb
Show file tree
Hide file tree
Showing 37 changed files with 274 additions and 168 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/php-cs-fixer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ jobs:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.1
php-version: 8.2
coverage: none
tools: php-cs-fixer:3.54.x, cs2pr
tools: php-cs-fixer:3.67.x, cs2pr

- name: Restore PHP-CS-Fixer cache
uses: actions/cache@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/phpstan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
php: [ "8.1", "8.3" ]
php: [ "8.2", "8.4" ]
steps:
- uses: actions/checkout@v4

Expand Down
9 changes: 6 additions & 3 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
php: ["8.1", "8.2", "8.3"]
stability: [--prefer-lowest, --prefer-stable]
include:
- php: "8.4"
stability: --prefer-stable
- php: "8.2"
stability: --prefer-lowest
env:
PHP_VERSION: ${{ matrix.php }}
DEPS_STRATEGY: ${{ matrix.stability }}
Expand Down Expand Up @@ -46,7 +49,7 @@ jobs:

- name: Run tests
run: |
export WITH_COVERAGE=$(if [[ ("${{ matrix.php }}" = "8.3") && ("${{ matrix.stability }}" = "--prefer-stable") ]]; then echo "true"; else echo "false"; fi)
export WITH_COVERAGE=$(if [[ ("${{ matrix.php }}" = "8.4") && ("${{ matrix.stability }}" = "--prefer-stable") ]]; then echo "true"; else echo "false"; fi)
echo "WITH_COVERAGE=${WITH_COVERAGE}" >> $GITHUB_ENV
make vendor
make tests
Expand Down
2 changes: 1 addition & 1 deletion .scrutinizer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ build:
analysis:
environment:
php:
version: 8.1
version: 8.4
cache:
disabled: false
directories:
Expand Down
18 changes: 9 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
DOCKER_COMPOSE ?= docker compose
EXEC_PHP = $(DOCKER_COMPOSE) run --rm -T php
PHP_VERSION ?= 8.2
PHP_VERSION ?= 8.4
DEPS_STRATEGY ?= --prefer-stable
COMPOSER = $(EXEC_PHP) composer
WITH_COVERAGE ?= "FALSE"
Expand All @@ -27,11 +27,11 @@ setup: build

tests: ## Run all tests
tests:
if [ $(WITH_COVERAGE) = true ]; then $(EXEC_PHP) vendor/bin/phpunit --coverage-clover clover.xml; else $(EXEC_PHP) vendor/bin/phpunit; fi
if [ $(WITH_COVERAGE) = true ]; then $(EXEC_PHP) vendor/bin/phpunit --display-deprecations --coverage-clover clover.xml; else $(EXEC_PHP) vendor/bin/phpunit --display-deprecations; fi

tests-dox: ## Run all tests in dox format
tests-dox:
if [ $(WITH_COVERAGE) = true ]; then $(EXEC_PHP) vendor/bin/phpunit --coverage-clover clover.xml --testdox; else $(EXEC_PHP) vendor/bin/phpunit --testdox; fi
if [ $(WITH_COVERAGE) = true ]; then $(EXEC_PHP) vendor/bin/phpunit --display-deprecations --coverage-clover clover.xml --testdox; else $(EXEC_PHP) vendor/bin/phpunit --display-deprecations --testdox; fi

# @TODO not optimized, it recreates a container for each example
examples-test:
Expand All @@ -45,11 +45,11 @@ examples-test:

tu: ## Run unit tests
tu: vendor
$(EXEC_PHP) vendor/bin/phpunit --exclude-group integration
$(EXEC_PHP) vendor/bin/phpunit --display-deprecations --exclude-group integration

ti: ## Run functional tests
ti: vendor
$(EXEC_PHP) vendor/bin/phpunit --group integration
$(EXEC_PHP) vendor/bin/phpunit --display-deprecations --group integration

.PHONY: tests tests-dox examples-test tu ti

Expand All @@ -59,12 +59,12 @@ vendor:
PHP_CS_FIXER = docker-compose run --rm -T php tools/php-cs-fixer/vendor/bin/php-cs-fixer fix -vv --allow-risky=yes

phpcs:
PHP_VERSION=8.1 $(EXEC_PHP) composer -d tools/php-cs-fixer update
PHP_VERSION=8.1 $(PHP_CS_FIXER) --dry-run
PHP_VERSION=8.2 $(EXEC_PHP) composer -d tools/php-cs-fixer update
PHP_VERSION=8.2 $(PHP_CS_FIXER) --dry-run

phpcbf:
PHP_VERSION=8.1 $(EXEC_PHP) composer -d tools/php-cs-fixer update
PHP_VERSION=8.1 $(PHP_CS_FIXER)
PHP_VERSION=8.2 $(EXEC_PHP) composer -d tools/php-cs-fixer update
PHP_VERSION=8.2 $(PHP_CS_FIXER)

phpstan: vendor
$(EXEC_PHP) vendor/bin/phpstan analyse src -c phpstan.neon -a vendor/autoload.php
Expand Down
26 changes: 12 additions & 14 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,26 @@
}
],
"require": {
"php": "^8.1",
"php": "^8.2",
"nyholm/psr7": "^1.3",
"psr/http-client": "^1.0",
"symfony/process": "^4.4.30 | ^5.0 |^6.0 | ^7.0",
"thecodingmachine/safe": "^1.0 | ^2.0",
"symfony/process": "^5 |^6 | ^7",
"webmozart/assert": "^1.11"
},
"require-dev": {
"aptoma/twig-markdown": "^3.0",
"cocur/slugify": "^3.2 || ^4.0",
"erusev/parsedown": "^1.7",
"erusev/parsedown-extra": "^0.8",
"phpstan/phpstan": "^1.2.0",
"phpstan/phpstan-strict-rules": "^1.1.0",
"phpstan/phpstan-webmozart-assert": "^1.0.0",
"phpstan/phpstan-phpunit": "^1.0.0",
"phpunit/phpunit": "^9.5",
"pixelrobin/php-feather": "^1.0",
"symfony/filesystem": "^4.4 || ^5.0 || ^6.0",
"symfony/finder": "^4.4 || ^5.0 || ^6.0",
"symfony/http-client": "^5.0 || ^6.0",
"thecodingmachine/phpstan-safe-rule": "^1.1"
"phpstan/phpstan": "^2",
"phpstan/phpstan-strict-rules": "^2",
"phpstan/phpstan-webmozart-assert": "^2",
"phpstan/phpstan-phpunit": "^2",
"phpunit/phpunit": "^11",
"pixelrobin/php-feather": "^2",
"symfony/filesystem": "^5 |^6 | ^7",
"symfony/finder": "^5 |^6 | ^7",
"symfony/http-client": "^5 |^6 | ^7"
},
"suggest": {
"symfony/http-client": "A PSR-18 Client implementation to use spellcheckers that relies on HTTP APIs"
Expand All @@ -51,7 +49,7 @@
"psr-4": {
"PhpSpellcheck\\": "src"
},
"files": [ "src/Text/functions.php" ]
"files": [ "src/Text/functions.php" , "src/Utils/php-functions.php" ]
},
"autoload-dev": {
"psr-4": {
Expand Down
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
services:
php:
image: tigitz/phpspellchecker:${PHP_VERSION:-8.1}
image: tigitz/phpspellchecker:${PHP_VERSION:-8.4}
build:
context: docker/php
args:
PHP_VERSION: ${PHP_VERSION:-8.1}
PHP_VERSION: ${PHP_VERSION:-8.4}
volumes:
- .:/usr/src/myapp
- ./cache:/root/composer/cache
Expand Down
23 changes: 13 additions & 10 deletions docker/php/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,19 @@ RUN apt-get update \
aspell-en \
aspell-ru \
libpspell-dev
RUN set -eux; \
case "$PHP_VERSION" in \
8.1*) pecl install xdebug-3.1.1;; \
*) pecl install xdebug-3.3.2;; \
esac
RUN docker-php-ext-configure pspell \
&& docker-php-ext-enable xdebug \
&& docker-php-ext-install pspell \
&& docker-php-ext-install zip \
&& rm -r /var/lib/apt/lists/*

RUN pecl channel-update pecl.php.net && \
pecl install xdebug-3.4.0 && \
docker-php-ext-enable xdebug

RUN if [ "${PHP_VERSION}" = "8.4" ]; then \
pecl install pspell; \
else \
docker-php-ext-configure pspell && \
docker-php-ext-install pspell; \
fi && \
docker-php-ext-enable pspell && \
rm -r /var/lib/apt/lists/*

RUN cp /usr/share/hunspell/en_US.aff /usr/share/hunspell/en_US.aff.orig \
&& cp /usr/share/hunspell/en_US.dic /usr/share/hunspell/en_US.dic.orig \
Expand Down
8 changes: 4 additions & 4 deletions docs/generate-docs.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,11 @@
}

// Generate index.html doc file from the readme while stripping some sections
$readme = \Safe\file_get_contents(__DIR__.'/../README.md');
$readme = \PhpSpellcheck\file_get_contents(__DIR__.'/../README.md');

$readme = \Safe\preg_replace('/(# Install[\s\S]+?)^# /m', '# ', $readme);
$readme = \Safe\preg_replace('/(# Usage[\s\S]+?)^# /m', '# ', $readme);
$readme = \Safe\preg_replace('/(# Testing[\s\S]+?)^# /m', '# ', $readme);
$readme = \PhpSpellcheck\preg_replace('/(# Install[\s\S]+?)^# /m', '# ', $readme);
$readme = \PhpSpellcheck\preg_replace('/(# Usage[\s\S]+?)^# /m', '# ', $readme);
$readme = \PhpSpellcheck\preg_replace('/(# Testing[\s\S]+?)^# /m', '# ', $readme);

$fs->dumpFile(
__DIR__.'/index.html',
Expand Down
15 changes: 15 additions & 0 deletions src/Exception/FilesystemException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace PhpSpellcheck\Exception;

class FilesystemException extends \ErrorException implements ExceptionInterface
{
public static function createFromPhpError(): self
{
$error = error_get_last();

return new self($error['message'] ?? 'An error occured', 0, $error['type'] ?? 1);
}
}
13 changes: 13 additions & 0 deletions src/Exception/JsonException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace PhpSpellcheck\Exception;

class JsonException extends \JsonException implements ExceptionInterface
{
public static function createFromPhpError(): self
{
return new self(json_last_error_msg(), json_last_error());
}
}
23 changes: 23 additions & 0 deletions src/Exception/PcreException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace PhpSpellcheck\Exception;

class PcreException extends \Exception implements ExceptionInterface
{
public static function createFromPhpError(): self
{
$errorMap = [
PREG_INTERNAL_ERROR => 'PREG_INTERNAL_ERROR: Internal error',
PREG_BACKTRACK_LIMIT_ERROR => 'PREG_BACKTRACK_LIMIT_ERROR: Backtrack limit reached',
PREG_RECURSION_LIMIT_ERROR => 'PREG_RECURSION_LIMIT_ERROR: Recursion limit reached',
PREG_BAD_UTF8_ERROR => 'PREG_BAD_UTF8_ERROR: Invalid UTF8 character',
PREG_BAD_UTF8_OFFSET_ERROR => 'PREG_BAD_UTF8_OFFSET_ERROR',
PREG_JIT_STACKLIMIT_ERROR => 'PREG_JIT_STACKLIMIT_ERROR',
];
$errMsg = $errorMap[preg_last_error()] ?? 'Unknown PCRE error: '.preg_last_error();

return new self($errMsg, preg_last_error());
}
}
4 changes: 2 additions & 2 deletions src/Exception/ProcessFailedException.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ class ProcessFailedException extends \RuntimeException implements ExceptionInter

public function __construct(
Process $process,
\Throwable $previous = null,
?\Throwable $previous = null,
string $failureReason = '',
int $code = 0
) {
$this->process = $process;

$message = \Safe\sprintf(
$message = \sprintf(
'Process with command "%s" has failed%s with exit code %d(%s)%s',
$process->getCommandLine(),
$process->isStarted() ? ' running' : '',
Expand Down
2 changes: 1 addition & 1 deletion src/Exception/ProcessHasErrorOutputException.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function __construct(string $errorOutput, string $parsedText, string $com
MSG;

parent::__construct(
\Safe\sprintf(
\sprintf(
$exceptionTemplateMessage,
$errorOutput,
$command,
Expand Down
2 changes: 1 addition & 1 deletion src/Misspelling.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public function __construct(
public function mergeSuggestions(array $suggestionsToAdd): MisspellingInterface
{
$mergedSuggestions = [];
$existingSuggestionsAsKeys = \Safe\array_flip($this->suggestions);
$existingSuggestionsAsKeys = array_flip($this->suggestions);
foreach ($suggestionsToAdd as $suggestionToAdd) {
if (!isset($existingSuggestionsAsKeys[$suggestionToAdd])) {
$this->suggestions[] = $suggestionToAdd;
Expand Down
4 changes: 2 additions & 2 deletions src/MisspellingHandler/EchoHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ class EchoHandler implements MisspellingHandlerInterface
public function handle(iterable $misspellings): void
{
foreach ($misspellings as $misspelling) {
$output = sprintf(
$output = \sprintf(
'word: %s | line: %d | offset: %d | suggestions: %s | context: %s' . PHP_EOL,
$misspelling->getWord(),
$misspelling->getLineNumber(),
$misspelling->getOffset(),
$misspelling->hasSuggestions() ? implode(',', $misspelling->getSuggestions()) : '',
\Safe\json_encode($misspelling->getContext())
\PhpSpellcheck\json_encode($misspelling->getContext(), flags: JSON_THROW_ON_ERROR)
);

echo $output;
Expand Down
2 changes: 1 addition & 1 deletion src/Source/Directory.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ private function getContents(): iterable
// When regex pattern is used, an array containing the file path in its first element is returned
$file = new \SplFileInfo(current($file));
} else {
throw new RuntimeException(\Safe\sprintf('Couldn\'t create "%s" object from the given file', \SplFileInfo::class));
throw new RuntimeException(\sprintf('Couldn\'t create "%s" object from the given file', \SplFileInfo::class));
}

if (!$file->isDir() && $file->getRealPath() !== false) {
Expand Down
4 changes: 2 additions & 2 deletions src/Source/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ public function __construct(string $filePath)
*/
public function toTexts(array $context = []): iterable
{
$context['filePath'] = \Safe\realpath($this->filePath);
$context['filePath'] = \PhpSpellcheck\realpath($this->filePath);

yield new Text($this->getFileContent(), $context);
}

private function getFileContent(): string
{
return \Safe\file_get_contents($this->filePath);
return \PhpSpellcheck\file_get_contents($this->filePath);
}
}
2 changes: 1 addition & 1 deletion src/Spellchecker/Aspell.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public function getSupportedLanguages(): iterable
$languages[$name] = true;
}
$languages = array_keys($languages);
\Safe\sort($languages);
sort($languages);

return $languages;
}
Expand Down
6 changes: 3 additions & 3 deletions src/Spellchecker/Hunspell.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public function getSupportedLanguages(): iterable
foreach ($output as $line) {
$line = trim($line);
if ('' === $line // Skip empty lines
|| \Safe\substr($line, -1) === ':' // Skip headers
|| substr($line, -1) === ':' // Skip headers
|| strpos($line, ':') !== false // Skip search path
) {
continue;
Expand All @@ -78,11 +78,11 @@ public function getSupportedLanguages(): iterable
// Skip MySpell hyphen files
continue;
}
$name = \Safe\preg_replace('/\.(aff|dic)$/', '', $name);
$name = \PhpSpellcheck\preg_replace('/\.(aff|dic)$/', '', $name);
$languages[$name] = true;
}
$languages = array_keys($languages);
\Safe\sort($languages);
sort($languages);

return $languages;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Spellchecker/Ispell.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public function getSupportedLanguages(): iterable
continue;
}

yield \Safe\substr($file, 0, -4);
yield substr($file, 0, -4);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Spellchecker/JamSpell.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public function check(string $text, array $languages, array $context): iterable
->createRequest('POST', $this->endpoint)
->withBody(Stream::create($text));

$spellcheckResponseAsArray = \Safe\json_decode($spellcheckResponse = $this->httpClient->sendRequest($request)->getBody()->getContents(), true);
$spellcheckResponseAsArray = json_decode($spellcheckResponse = $this->httpClient->sendRequest($request)->getBody()->getContents(), true, flags: JSON_THROW_ON_ERROR);
Assert::isArray($spellcheckResponseAsArray);

// @TODO use json api validation schema
Expand Down
Loading

0 comments on commit bdfd1fb

Please sign in to comment.