diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4ec9c7e6..791638c5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,11 +15,11 @@ jobs: strategy: fail-fast: false matrix: - php-versions: ['7.4', '8.0', '8.1', '8.2','8.3'] + php-versions: ['8.1', '8.2','8.3', '8.4', '8.5'] experimental: [false] - include: - - php-versions: '8.4' - experimental: true + #include: + # - php-versions: '8.4' + # experimental: true steps: - name: Checkout repository uses: actions/checkout@v4 @@ -56,7 +56,7 @@ jobs: strategy: fail-fast: true matrix: - php-versions: ['8.3'] + php-versions: ['8.5'] steps: - name: Checkout repository uses: actions/checkout@v4 diff --git a/README.md b/README.md index 014cf08f..112164e5 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ A PHP library for improving your web typography: ## Requirements -* PHP 7.4.0 or above +* PHP 8.1.0 or above * The `mbstring` extension ## Installation diff --git a/composer.json b/composer.json index 4ff08b7c..bf53b525 100644 --- a/composer.json +++ b/composer.json @@ -19,24 +19,24 @@ ], "require": { - "php": ">=7.4.0", + "php": ">=8.1.0", "ext-pcre": "*", "ext-mbstring": "*", "masterminds/html5": "^2.5.0" }, "require-dev": { "ext-curl": "*", - "phpunit/phpunit": "9.*|10.*", + "phpunit/phpunit": "9.*|10.*|11.*|12.*|13.*", "brain/monkey": "^2.2.0", - "squizlabs/php_codesniffer": "^3", + "squizlabs/php_codesniffer": "^3|^4", "wp-coding-standards/wpcs": "^3", "phpcompatibility/php-compatibility": "^9.0", "dealerdirect/phpcodesniffer-composer-installer": "^1.0", - "phpstan/phpstan": "^1.9", + "phpstan/phpstan": "^2", "phpbench/phpbench": "^0.17||^1.0@dev", "mikey179/vfsstream": "~1", - "mundschenk-at/phpunit-cross-version": "dev-master", - "phpstan/phpstan-mockery": "^1.1", + "mundschenk-at/phpunit-cross-version": "dev-main", + "phpstan/phpstan-mockery": "^2", "phpstan/extension-installer": "^1.2" }, diff --git a/phpcs.xml b/phpcs.xml index e89d56c8..216f2be6 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -7,7 +7,7 @@ * See https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards/blob/develop/WordPress-Core/ruleset.xml - + @@ -24,7 +24,17 @@ - + + + + + + + + + + + diff --git a/src/bin/class-file-operations.php b/src/bin/class-file-operations.php index dc3530b6..02d899b2 100644 --- a/src/bin/class-file-operations.php +++ b/src/bin/class-file-operations.php @@ -2,7 +2,7 @@ /** * This file is part of PHP-Typography. * - * Copyright 2017-2019 Peter Putzer. + * Copyright 2017-2026 Peter Putzer. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -42,8 +42,10 @@ abstract class File_Operations { * @param string $url Required. * * @return int + * + * @phpstan-param non-empty-string $url */ - public static function get_http_response_code( $url ) { + public static function get_http_response_code( string $url ) { $curl = curl_init(); curl_setopt_array( @@ -54,9 +56,6 @@ public static function get_http_response_code( $url ) { ] ); curl_exec( $curl ); - $response_code = curl_getinfo( $curl, CURLINFO_HTTP_CODE ); - curl_close( $curl ); - - return $response_code; + return curl_getinfo( $curl, CURLINFO_HTTP_CODE ); } } diff --git a/src/bin/class-pattern-converter.php b/src/bin/class-pattern-converter.php index 62c0fd3a..75aba094 100644 --- a/src/bin/class-pattern-converter.php +++ b/src/bin/class-pattern-converter.php @@ -2,7 +2,7 @@ /** * This file is part of PHP-Typography. * - * Copyright 2015-2024 Peter Putzer. + * Copyright 2015-2026 Peter Putzer. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -45,21 +45,21 @@ class Pattern_Converter { * * @var string[] */ - protected $urls; + protected array $urls; /** * Human-readable language name. * * @var string */ - protected $language; + protected string $language; /** * A word character class in PCRE2 syntax. * * @var string */ - protected $word_class; + protected string $word_class; /** * Creates a new converter object. @@ -67,7 +67,7 @@ class Pattern_Converter { * @param string|string[] $urls The TeX pattern file URL(s). * @param string $language A human-readable language name. */ - public function __construct( $urls, $language ) { + public function __construct( $urls, string $language ) { $this->urls = (array) $urls; $this->language = $language; @@ -109,7 +109,7 @@ public function __construct( $urls, $language ) { * @param string $pattern TeX hyphenation pattern. * @return string */ - protected function get_segment( $pattern ) { + protected function get_segment( string $pattern ) { return (string) \preg_replace( '/[0-9]/', '', \str_replace( '.', '_', $pattern ) ); } @@ -122,7 +122,7 @@ protected function get_segment( $pattern ) { * * @return string */ - protected function get_sequence( $pattern ) { + protected function get_sequence( string $pattern ) { $characters = \mb_str_split( \str_replace( '.', '_', $pattern ) ); $result = []; @@ -196,7 +196,7 @@ protected function format_results( array $patterns, array $exceptions, array $co } /** - * Try to match squences of TeX hyphenation exceptions. + * Try to match sequences of TeX hyphenation exceptions. * * @param string $line A line from the TeX pattern file. * @param string[] $exceptions { @@ -210,7 +210,7 @@ protected function format_results( array $patterns, array $exceptions, array $co * * @return bool */ - protected function match_exceptions( $line, array &$exceptions, $line_no = 0 ) { + protected function match_exceptions( string $line, array &$exceptions, int $line_no = 0 ) { $continue_reading_exceptions = true; if ( \preg_match( "/^\s*({$this->word_class}+)\s*}\s*(?:%.*)?$/u", $line, $matches ) ) { @@ -248,7 +248,7 @@ protected function match_exceptions( $line, array &$exceptions, $line_no = 0 ) { * * @return bool Whether the parser should stay in "reading patterns" mode. */ - protected function match_patterns( $line, array &$patterns, $line_no = 0 ) { + protected function match_patterns( string $line, array &$patterns, int $line_no = 0 ) { $continue_reading_patterns = true; if ( \preg_match( "/^\s*({$this->word_class}+)\s*\}\s*(?:%.*)?$/u", $line, $matches ) ) { @@ -327,7 +327,7 @@ protected function parse_line( string $line, int $line_no, array &$patterns, arr * * @return string */ - protected function expand_macros( $line, array $macros ) { + protected function expand_macros( string $line, array $macros ) { if ( 0 < \preg_match_all( '/\\\(?\w+)\{(?[^\}]+)\}/u', $line, $matches, \PREG_SET_ORDER ) ) { foreach ( $matches as $m ) { if ( ! empty( $macros[ $m['name'] ] ) ) { @@ -348,7 +348,7 @@ protected function expand_macros( $line, array $macros ) { * * @return array */ - private static function split_at_whitespace( $line ) { + private static function split_at_whitespace( string $line ) { return \preg_split( '/\s+/Su', $line, -1, PREG_SPLIT_NO_EMPTY ) ?: []; // phpcs:ignore Universal.Operators.DisallowShortTernary -- We can safely assume an array here, as long as $line convertible to a string. } @@ -386,8 +386,10 @@ public function convert() { * @throws Invalid_Path_Exception Thrown when file does not exist. * @throws Invalid_File_Exception Thrown when file exists, but is not readable. */ - protected function convert_single_file( $url, &$patterns, &$exceptions, &$comments ): void { - if ( ! \file_exists( $url ) && 404 === File_Operations::get_http_response_code( $url ) ) { + protected function convert_single_file( string $url, array &$patterns, array &$exceptions, array &$comments ): void { + if ( empty( $url ) ) { + throw new Invalid_Path_Exception( "Error: empty pattern file URL'\n" ); + } elseif ( ! \file_exists( $url ) && 404 === File_Operations::get_http_response_code( $url ) ) { throw new Invalid_Path_Exception( "Error: unknown pattern file '{$url}'\n" ); } diff --git a/src/class-php-typography.php b/src/class-php-typography.php index 4d005760..753e0c29 100644 --- a/src/class-php-typography.php +++ b/src/class-php-typography.php @@ -2,7 +2,7 @@ /** * This file is part of PHP-Typography. * - * Copyright 2014-2024 Peter Putzer. + * Copyright 2014-2026 Peter Putzer. * Copyright 2009-2011 KINGdesk, LLC. * * This program is free software; you can redistribute it and/or modify @@ -342,6 +342,11 @@ public function query_tags_to_ignore( \DOMXPath $xpath, \DOMNode $initial_node, if ( ! empty( $query_parts ) ) { $ignore_query = \implode( ' | ', $query_parts ); + /** + * No DOMNamespaceNodes here. + * + * @phpstan-var \DOMNodeList<\DOMNode> $nodelist + */ $nodelist = $xpath->query( $ignore_query, $initial_node ); if ( false !== $nodelist ) { $elements = DOM::nodelist_to_array( $nodelist ); diff --git a/src/class-settings.php b/src/class-settings.php index 7a873baa..081c03f2 100644 --- a/src/class-settings.php +++ b/src/class-settings.php @@ -2,7 +2,7 @@ /** * This file is part of PHP-Typography. * - * Copyright 2014-2024 Peter Putzer. + * Copyright 2014-2026 Peter Putzer. * Copyright 2009-2011 KINGdesk, LLC. * * This program is free software; you can redistribute it and/or modify @@ -194,7 +194,7 @@ * @method void set_ignore_parser_errors( bool $on = false ) Enable lenient parser error handling (HTML is "best guess" if enabled). * @method void set_parser_errors_handler( callable $handler = null ) Sets an optional handler for parser errors. The callable takes an array of error strings as its parameter. Invalid callbacks will be silently ignored. * - * @phpstan-type Property_Definition array{property:string, name:string, default?:mixed, verify?:callable-string} + * @phpstan-type Property_Definition array{property:string, name:string, default?:mixed, verify?:callable-string, ...} */ class Settings { @@ -611,7 +611,7 @@ class Settings { * * @since 7.0.0 * - * @var array + * @var array */ protected array $virtual_setters = []; diff --git a/src/fixes/node-fixes/class-smart-marks-fix.php b/src/fixes/node-fixes/class-smart-marks-fix.php index bd684571..5e35d464 100644 --- a/src/fixes/node-fixes/class-smart-marks-fix.php +++ b/src/fixes/node-fixes/class-smart-marks-fix.php @@ -2,7 +2,7 @@ /** * This file is part of PHP-Typography. * - * Copyright 2017-2024 Peter Putzer. + * Copyright 2017-2026 Peter Putzer. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -109,7 +109,7 @@ public function apply( \DOMText $textnode, Settings $settings, $is_title ) { // Replace marks. $node_data = \str_replace( $this->marks, $this->replacements, $node_data ); - // Un-escape escaped sequences & resetore textnode content. + // Un-escape escaped sequences & restore textnode content. $textnode->data = \str_replace( RE::ESCAPE_MARKER, '', $node_data ); } } diff --git a/src/fixes/token-fixes/class-wrap-urls-fix.php b/src/fixes/token-fixes/class-wrap-urls-fix.php index e8b7350d..ca7da22c 100644 --- a/src/fixes/token-fixes/class-wrap-urls-fix.php +++ b/src/fixes/token-fixes/class-wrap-urls-fix.php @@ -45,7 +45,7 @@ */ class Wrap_URLs_Fix extends Hyphenate_Fix { // Valid URL schemes. - const URL_SCHEME = '(?:https?|ftps?|file|nfs|feed|itms|itpc)'; + const URL_SCHEME = '(?:https?|ftps?|file|nfs|feed|itms|itpc)'; // # spellchecker:disable-line const WRAP_URLS_DOMAIN_PARTS = '#(\-|\.)#'; diff --git a/tests/class-hyphenator-test.php b/tests/class-hyphenator-test.php index ad570fe7..a139e084 100644 --- a/tests/class-hyphenator-test.php +++ b/tests/class-hyphenator-test.php @@ -2,7 +2,7 @@ /** * This file is part of PHP-Typography. * - * Copyright 2016-2024 Peter Putzer. + * Copyright 2016-2026 Peter Putzer. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -633,10 +633,8 @@ public function test_hyphenate_no_exceptions_at_all() { // Unset some internal stuff. $ref = new \ReflectionClass( Hyphenator::class ); $prop = $ref->getProperty( 'pattern_exceptions' ); - $prop->setAccessible( true ); $prop->setValue( $this->h, [] ); $prop = $ref->getProperty( 'merged_exception_patterns' ); - $prop->setAccessible( true ); $prop->setValue( $this->h, null ); // Again, no punctuation due to the fake tokenization.