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.