├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── composer.lock ├── phpstan.neon ├── phpunit.xml.dist ├── resources └── libstemmer.h ├── src ├── Adapter │ └── Libstemmer.php ├── Enum │ └── CharacterEncodingEnum.php ├── Exception │ └── UnavailableAlgorithmException.php ├── Stemmer.php └── StemmerInterface.php └── tests ├── Adapter └── LibstemmerTest.php └── StemmerTest.php /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: ~ 5 | push: 6 | branches: 7 | - master 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | max-parallel: 1 15 | matrix: 16 | container: 17 | - "amaccis/php-libstemmer:8.1.32-3.0.0" 18 | - "amaccis/php-libstemmer:8.2.28-3.0.0" 19 | - "amaccis/php-libstemmer:8.3.20-3.0.0" 20 | - "amaccis/php-libstemmer:8.4.6-3.0.0" 21 | 22 | container: 23 | image: ${{ matrix.container }} 24 | 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v3 28 | - name: Install dependencies 29 | run: composer install 30 | - name: Run tests 31 | run: vendor/bin/phpunit 32 | - name: Run static analysis 33 | run: vendor/bin/phpstan -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | .idea 3 | .phpunit.result.cache 4 | .phpunit.cache 5 | coverage -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Andrea Maccis 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # php-stemmer 2 | 3 | [![PHP Version](https://img.shields.io/badge/php-%5E8.1-blue.svg)](https://img.shields.io/badge/php-%5E8.1-blue.svg) 4 | ![CI](https://github.com/amaccis/php-stemmer/workflows/CI/badge.svg) 5 | 6 | ## What is PHP Stemmer? 7 | PHP Stemmer is a PHP interface to the stemming algorithms from the [Snowball project](https://snowballstem.org/), largely inspired by Richard Boulton's [PyStemmer](https://github.com/snowballstem/pystemmer). 8 | It uses FFI (PHP >= 7.4.0) and expects to find the file libstemmer.so (a version of [Libstemmer](https://snowballstem.org/dist/libstemmer_c.tgz) compiled as shared library) in LD_LIBRARY_PATH. 9 | In order to set up this kind of environment you can take a look at [docker-php-libstemmer](https://github.com/amaccis/docker-php-libstemmer) Dockerfile or you can use the corresponding docker image: [amaccis/php-libstemmer](https://hub.docker.com/r/amaccis/php-libstemmer) 10 | 11 | ## Installation 12 | 13 | PHP Stemmer is available on [Packagist](http://packagist.org/packages/amaccis/php-stemmer), 14 | you can install it using [Composer](http://getcomposer.org). 15 | 16 | ```shell 17 | composer require amaccis/php-stemmer 18 | ``` 19 | 20 | ## Usage 21 | 22 | ```php 23 | 33 | string(6) "arabic" 34 | [1] => 35 | string(8) "armenian" 36 | [2] => 37 | string(6) "basque" 38 | [3] => 39 | string(7) "catalan" 40 | [4] => 41 | string(6) "danish" 42 | [5] => 43 | string(5) "dutch" 44 | [6] => 45 | string(7) "english" 46 | [7] => 47 | string(7) "finnish" 48 | [8] => 49 | string(6) "french" 50 | [9] => 51 | string(6) "german" 52 | [10] => 53 | string(5) "greek" 54 | [11] => 55 | string(5) "hindi" 56 | [12] => 57 | string(9) "hungarian" 58 | [13] => 59 | string(10) "indonesian" 60 | [14] => 61 | string(5) "irish" 62 | [15] => 63 | string(7) "italian" 64 | [16] => 65 | string(10) "lithuanian" 66 | [17] => 67 | string(6) "nepali" 68 | [18] => 69 | string(9) "norwegian" 70 | [19] => 71 | string(6) "porter" 72 | [20] => 73 | string(10) "portuguese" 74 | [21] => 75 | string(8) "romanian" 76 | [22] => 77 | string(7) "russian" 78 | [23] => 79 | string(7) "serbian" 80 | [24] => 81 | string(7) "spanish" 82 | [25] => 83 | string(7) "swedish" 84 | [26] => 85 | string(5) "tamil" 86 | [27] => 87 | string(7) "turkish" 88 | [28] => 89 | string(7) "yiddish" 90 | } 91 | */ 92 | 93 | $algorithm = "english"; 94 | $word = "cycling"; 95 | $stemmer = new Stemmer($algorithm); // default character encoding is UTF-8 96 | $stem = $stemmer->stemWord($word); 97 | var_dump($stem); 98 | /* 99 | string(4) "cycl" 100 | */ 101 | 102 | $algorithm = "basque"; 103 | $word = "aberatsenetakoa"; 104 | $stemmer = new Stemmer($algorithm, CharacterEncodingEnum::ISO_8859_1); 105 | $stem = $stemmer->stemWord($word); 106 | var_dump($stem); 107 | /* 108 | string(8) "aberatse" 109 | */ 110 | ``` 111 | 112 | ## License 113 | All files are MIT © [Andrea Maccis](https://twitter.com/andreamaccis) except for _resources/libstemmer.h_ BSD-3 © [Snowball Project](https://github.com/snowballstem/snowball). -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "amaccis/php-stemmer", 3 | "type": "library", 4 | "description": "A PHP interface to the Snowball stemming algorithms", 5 | "keywords": [ 6 | "snowball", 7 | "stem", 8 | "stemmer", 9 | "stemming" 10 | ], 11 | "license": "MIT", 12 | "authors": [ 13 | { 14 | "name": "Andrea Maccis", 15 | "email": "andrea.maccis@gmail.com" 16 | } 17 | ], 18 | "autoload": { 19 | "psr-4": { 20 | "Amaccis\\Stemmer\\": "src/" 21 | } 22 | }, 23 | "autoload-dev": { 24 | "psr-4": { 25 | "Amaccis\\Stemmer\\Tests\\": "tests/" 26 | } 27 | }, 28 | "require": { 29 | "php": "^8.1.0", 30 | "ext-ffi": "*" 31 | }, 32 | "require-dev": { 33 | "phpunit/phpunit": "^10", 34 | "phpstan/phpstan": "^1", 35 | "ext-iconv": "*" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "6e8f803064686ca3c2168575a9c89cb5", 8 | "packages": [], 9 | "packages-dev": [ 10 | { 11 | "name": "myclabs/deep-copy", 12 | "version": "1.13.1", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/myclabs/DeepCopy.git", 16 | "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c", 21 | "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c", 22 | "shasum": "" 23 | }, 24 | "require": { 25 | "php": "^7.1 || ^8.0" 26 | }, 27 | "conflict": { 28 | "doctrine/collections": "<1.6.8", 29 | "doctrine/common": "<2.13.3 || >=3 <3.2.2" 30 | }, 31 | "require-dev": { 32 | "doctrine/collections": "^1.6.8", 33 | "doctrine/common": "^2.13.3 || ^3.2.2", 34 | "phpspec/prophecy": "^1.10", 35 | "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" 36 | }, 37 | "type": "library", 38 | "autoload": { 39 | "files": [ 40 | "src/DeepCopy/deep_copy.php" 41 | ], 42 | "psr-4": { 43 | "DeepCopy\\": "src/DeepCopy/" 44 | } 45 | }, 46 | "notification-url": "https://packagist.org/downloads/", 47 | "license": [ 48 | "MIT" 49 | ], 50 | "description": "Create deep copies (clones) of your objects", 51 | "keywords": [ 52 | "clone", 53 | "copy", 54 | "duplicate", 55 | "object", 56 | "object graph" 57 | ], 58 | "support": { 59 | "issues": "https://github.com/myclabs/DeepCopy/issues", 60 | "source": "https://github.com/myclabs/DeepCopy/tree/1.13.1" 61 | }, 62 | "funding": [ 63 | { 64 | "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", 65 | "type": "tidelift" 66 | } 67 | ], 68 | "time": "2025-04-29T12:36:36+00:00" 69 | }, 70 | { 71 | "name": "nikic/php-parser", 72 | "version": "v5.4.0", 73 | "source": { 74 | "type": "git", 75 | "url": "https://github.com/nikic/PHP-Parser.git", 76 | "reference": "447a020a1f875a434d62f2a401f53b82a396e494" 77 | }, 78 | "dist": { 79 | "type": "zip", 80 | "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", 81 | "reference": "447a020a1f875a434d62f2a401f53b82a396e494", 82 | "shasum": "" 83 | }, 84 | "require": { 85 | "ext-ctype": "*", 86 | "ext-json": "*", 87 | "ext-tokenizer": "*", 88 | "php": ">=7.4" 89 | }, 90 | "require-dev": { 91 | "ircmaxell/php-yacc": "^0.0.7", 92 | "phpunit/phpunit": "^9.0" 93 | }, 94 | "bin": [ 95 | "bin/php-parse" 96 | ], 97 | "type": "library", 98 | "extra": { 99 | "branch-alias": { 100 | "dev-master": "5.0-dev" 101 | } 102 | }, 103 | "autoload": { 104 | "psr-4": { 105 | "PhpParser\\": "lib/PhpParser" 106 | } 107 | }, 108 | "notification-url": "https://packagist.org/downloads/", 109 | "license": [ 110 | "BSD-3-Clause" 111 | ], 112 | "authors": [ 113 | { 114 | "name": "Nikita Popov" 115 | } 116 | ], 117 | "description": "A PHP parser written in PHP", 118 | "keywords": [ 119 | "parser", 120 | "php" 121 | ], 122 | "support": { 123 | "issues": "https://github.com/nikic/PHP-Parser/issues", 124 | "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" 125 | }, 126 | "time": "2024-12-30T11:07:19+00:00" 127 | }, 128 | { 129 | "name": "phar-io/manifest", 130 | "version": "2.0.4", 131 | "source": { 132 | "type": "git", 133 | "url": "https://github.com/phar-io/manifest.git", 134 | "reference": "54750ef60c58e43759730615a392c31c80e23176" 135 | }, 136 | "dist": { 137 | "type": "zip", 138 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", 139 | "reference": "54750ef60c58e43759730615a392c31c80e23176", 140 | "shasum": "" 141 | }, 142 | "require": { 143 | "ext-dom": "*", 144 | "ext-libxml": "*", 145 | "ext-phar": "*", 146 | "ext-xmlwriter": "*", 147 | "phar-io/version": "^3.0.1", 148 | "php": "^7.2 || ^8.0" 149 | }, 150 | "type": "library", 151 | "extra": { 152 | "branch-alias": { 153 | "dev-master": "2.0.x-dev" 154 | } 155 | }, 156 | "autoload": { 157 | "classmap": [ 158 | "src/" 159 | ] 160 | }, 161 | "notification-url": "https://packagist.org/downloads/", 162 | "license": [ 163 | "BSD-3-Clause" 164 | ], 165 | "authors": [ 166 | { 167 | "name": "Arne Blankerts", 168 | "email": "arne@blankerts.de", 169 | "role": "Developer" 170 | }, 171 | { 172 | "name": "Sebastian Heuer", 173 | "email": "sebastian@phpeople.de", 174 | "role": "Developer" 175 | }, 176 | { 177 | "name": "Sebastian Bergmann", 178 | "email": "sebastian@phpunit.de", 179 | "role": "Developer" 180 | } 181 | ], 182 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", 183 | "support": { 184 | "issues": "https://github.com/phar-io/manifest/issues", 185 | "source": "https://github.com/phar-io/manifest/tree/2.0.4" 186 | }, 187 | "funding": [ 188 | { 189 | "url": "https://github.com/theseer", 190 | "type": "github" 191 | } 192 | ], 193 | "time": "2024-03-03T12:33:53+00:00" 194 | }, 195 | { 196 | "name": "phar-io/version", 197 | "version": "3.2.1", 198 | "source": { 199 | "type": "git", 200 | "url": "https://github.com/phar-io/version.git", 201 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" 202 | }, 203 | "dist": { 204 | "type": "zip", 205 | "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", 206 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", 207 | "shasum": "" 208 | }, 209 | "require": { 210 | "php": "^7.2 || ^8.0" 211 | }, 212 | "type": "library", 213 | "autoload": { 214 | "classmap": [ 215 | "src/" 216 | ] 217 | }, 218 | "notification-url": "https://packagist.org/downloads/", 219 | "license": [ 220 | "BSD-3-Clause" 221 | ], 222 | "authors": [ 223 | { 224 | "name": "Arne Blankerts", 225 | "email": "arne@blankerts.de", 226 | "role": "Developer" 227 | }, 228 | { 229 | "name": "Sebastian Heuer", 230 | "email": "sebastian@phpeople.de", 231 | "role": "Developer" 232 | }, 233 | { 234 | "name": "Sebastian Bergmann", 235 | "email": "sebastian@phpunit.de", 236 | "role": "Developer" 237 | } 238 | ], 239 | "description": "Library for handling version information and constraints", 240 | "support": { 241 | "issues": "https://github.com/phar-io/version/issues", 242 | "source": "https://github.com/phar-io/version/tree/3.2.1" 243 | }, 244 | "time": "2022-02-21T01:04:05+00:00" 245 | }, 246 | { 247 | "name": "phpstan/phpstan", 248 | "version": "1.12.25", 249 | "source": { 250 | "type": "git", 251 | "url": "https://github.com/phpstan/phpstan.git", 252 | "reference": "e310849a19e02b8bfcbb63147f495d8f872dd96f" 253 | }, 254 | "dist": { 255 | "type": "zip", 256 | "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e310849a19e02b8bfcbb63147f495d8f872dd96f", 257 | "reference": "e310849a19e02b8bfcbb63147f495d8f872dd96f", 258 | "shasum": "" 259 | }, 260 | "require": { 261 | "php": "^7.2|^8.0" 262 | }, 263 | "conflict": { 264 | "phpstan/phpstan-shim": "*" 265 | }, 266 | "bin": [ 267 | "phpstan", 268 | "phpstan.phar" 269 | ], 270 | "type": "library", 271 | "autoload": { 272 | "files": [ 273 | "bootstrap.php" 274 | ] 275 | }, 276 | "notification-url": "https://packagist.org/downloads/", 277 | "license": [ 278 | "MIT" 279 | ], 280 | "description": "PHPStan - PHP Static Analysis Tool", 281 | "keywords": [ 282 | "dev", 283 | "static analysis" 284 | ], 285 | "support": { 286 | "docs": "https://phpstan.org/user-guide/getting-started", 287 | "forum": "https://github.com/phpstan/phpstan/discussions", 288 | "issues": "https://github.com/phpstan/phpstan/issues", 289 | "security": "https://github.com/phpstan/phpstan/security/policy", 290 | "source": "https://github.com/phpstan/phpstan-src" 291 | }, 292 | "funding": [ 293 | { 294 | "url": "https://github.com/ondrejmirtes", 295 | "type": "github" 296 | }, 297 | { 298 | "url": "https://github.com/phpstan", 299 | "type": "github" 300 | } 301 | ], 302 | "time": "2025-04-27T12:20:45+00:00" 303 | }, 304 | { 305 | "name": "phpunit/php-code-coverage", 306 | "version": "10.1.16", 307 | "source": { 308 | "type": "git", 309 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 310 | "reference": "7e308268858ed6baedc8704a304727d20bc07c77" 311 | }, 312 | "dist": { 313 | "type": "zip", 314 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", 315 | "reference": "7e308268858ed6baedc8704a304727d20bc07c77", 316 | "shasum": "" 317 | }, 318 | "require": { 319 | "ext-dom": "*", 320 | "ext-libxml": "*", 321 | "ext-xmlwriter": "*", 322 | "nikic/php-parser": "^4.19.1 || ^5.1.0", 323 | "php": ">=8.1", 324 | "phpunit/php-file-iterator": "^4.1.0", 325 | "phpunit/php-text-template": "^3.0.1", 326 | "sebastian/code-unit-reverse-lookup": "^3.0.0", 327 | "sebastian/complexity": "^3.2.0", 328 | "sebastian/environment": "^6.1.0", 329 | "sebastian/lines-of-code": "^2.0.2", 330 | "sebastian/version": "^4.0.1", 331 | "theseer/tokenizer": "^1.2.3" 332 | }, 333 | "require-dev": { 334 | "phpunit/phpunit": "^10.1" 335 | }, 336 | "suggest": { 337 | "ext-pcov": "PHP extension that provides line coverage", 338 | "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" 339 | }, 340 | "type": "library", 341 | "extra": { 342 | "branch-alias": { 343 | "dev-main": "10.1.x-dev" 344 | } 345 | }, 346 | "autoload": { 347 | "classmap": [ 348 | "src/" 349 | ] 350 | }, 351 | "notification-url": "https://packagist.org/downloads/", 352 | "license": [ 353 | "BSD-3-Clause" 354 | ], 355 | "authors": [ 356 | { 357 | "name": "Sebastian Bergmann", 358 | "email": "sebastian@phpunit.de", 359 | "role": "lead" 360 | } 361 | ], 362 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 363 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 364 | "keywords": [ 365 | "coverage", 366 | "testing", 367 | "xunit" 368 | ], 369 | "support": { 370 | "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", 371 | "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", 372 | "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16" 373 | }, 374 | "funding": [ 375 | { 376 | "url": "https://github.com/sebastianbergmann", 377 | "type": "github" 378 | } 379 | ], 380 | "time": "2024-08-22T04:31:57+00:00" 381 | }, 382 | { 383 | "name": "phpunit/php-file-iterator", 384 | "version": "4.1.0", 385 | "source": { 386 | "type": "git", 387 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 388 | "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" 389 | }, 390 | "dist": { 391 | "type": "zip", 392 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", 393 | "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", 394 | "shasum": "" 395 | }, 396 | "require": { 397 | "php": ">=8.1" 398 | }, 399 | "require-dev": { 400 | "phpunit/phpunit": "^10.0" 401 | }, 402 | "type": "library", 403 | "extra": { 404 | "branch-alias": { 405 | "dev-main": "4.0-dev" 406 | } 407 | }, 408 | "autoload": { 409 | "classmap": [ 410 | "src/" 411 | ] 412 | }, 413 | "notification-url": "https://packagist.org/downloads/", 414 | "license": [ 415 | "BSD-3-Clause" 416 | ], 417 | "authors": [ 418 | { 419 | "name": "Sebastian Bergmann", 420 | "email": "sebastian@phpunit.de", 421 | "role": "lead" 422 | } 423 | ], 424 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 425 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 426 | "keywords": [ 427 | "filesystem", 428 | "iterator" 429 | ], 430 | "support": { 431 | "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", 432 | "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", 433 | "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" 434 | }, 435 | "funding": [ 436 | { 437 | "url": "https://github.com/sebastianbergmann", 438 | "type": "github" 439 | } 440 | ], 441 | "time": "2023-08-31T06:24:48+00:00" 442 | }, 443 | { 444 | "name": "phpunit/php-invoker", 445 | "version": "4.0.0", 446 | "source": { 447 | "type": "git", 448 | "url": "https://github.com/sebastianbergmann/php-invoker.git", 449 | "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" 450 | }, 451 | "dist": { 452 | "type": "zip", 453 | "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", 454 | "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", 455 | "shasum": "" 456 | }, 457 | "require": { 458 | "php": ">=8.1" 459 | }, 460 | "require-dev": { 461 | "ext-pcntl": "*", 462 | "phpunit/phpunit": "^10.0" 463 | }, 464 | "suggest": { 465 | "ext-pcntl": "*" 466 | }, 467 | "type": "library", 468 | "extra": { 469 | "branch-alias": { 470 | "dev-main": "4.0-dev" 471 | } 472 | }, 473 | "autoload": { 474 | "classmap": [ 475 | "src/" 476 | ] 477 | }, 478 | "notification-url": "https://packagist.org/downloads/", 479 | "license": [ 480 | "BSD-3-Clause" 481 | ], 482 | "authors": [ 483 | { 484 | "name": "Sebastian Bergmann", 485 | "email": "sebastian@phpunit.de", 486 | "role": "lead" 487 | } 488 | ], 489 | "description": "Invoke callables with a timeout", 490 | "homepage": "https://github.com/sebastianbergmann/php-invoker/", 491 | "keywords": [ 492 | "process" 493 | ], 494 | "support": { 495 | "issues": "https://github.com/sebastianbergmann/php-invoker/issues", 496 | "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" 497 | }, 498 | "funding": [ 499 | { 500 | "url": "https://github.com/sebastianbergmann", 501 | "type": "github" 502 | } 503 | ], 504 | "time": "2023-02-03T06:56:09+00:00" 505 | }, 506 | { 507 | "name": "phpunit/php-text-template", 508 | "version": "3.0.1", 509 | "source": { 510 | "type": "git", 511 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 512 | "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" 513 | }, 514 | "dist": { 515 | "type": "zip", 516 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", 517 | "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", 518 | "shasum": "" 519 | }, 520 | "require": { 521 | "php": ">=8.1" 522 | }, 523 | "require-dev": { 524 | "phpunit/phpunit": "^10.0" 525 | }, 526 | "type": "library", 527 | "extra": { 528 | "branch-alias": { 529 | "dev-main": "3.0-dev" 530 | } 531 | }, 532 | "autoload": { 533 | "classmap": [ 534 | "src/" 535 | ] 536 | }, 537 | "notification-url": "https://packagist.org/downloads/", 538 | "license": [ 539 | "BSD-3-Clause" 540 | ], 541 | "authors": [ 542 | { 543 | "name": "Sebastian Bergmann", 544 | "email": "sebastian@phpunit.de", 545 | "role": "lead" 546 | } 547 | ], 548 | "description": "Simple template engine.", 549 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 550 | "keywords": [ 551 | "template" 552 | ], 553 | "support": { 554 | "issues": "https://github.com/sebastianbergmann/php-text-template/issues", 555 | "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", 556 | "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" 557 | }, 558 | "funding": [ 559 | { 560 | "url": "https://github.com/sebastianbergmann", 561 | "type": "github" 562 | } 563 | ], 564 | "time": "2023-08-31T14:07:24+00:00" 565 | }, 566 | { 567 | "name": "phpunit/php-timer", 568 | "version": "6.0.0", 569 | "source": { 570 | "type": "git", 571 | "url": "https://github.com/sebastianbergmann/php-timer.git", 572 | "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" 573 | }, 574 | "dist": { 575 | "type": "zip", 576 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", 577 | "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", 578 | "shasum": "" 579 | }, 580 | "require": { 581 | "php": ">=8.1" 582 | }, 583 | "require-dev": { 584 | "phpunit/phpunit": "^10.0" 585 | }, 586 | "type": "library", 587 | "extra": { 588 | "branch-alias": { 589 | "dev-main": "6.0-dev" 590 | } 591 | }, 592 | "autoload": { 593 | "classmap": [ 594 | "src/" 595 | ] 596 | }, 597 | "notification-url": "https://packagist.org/downloads/", 598 | "license": [ 599 | "BSD-3-Clause" 600 | ], 601 | "authors": [ 602 | { 603 | "name": "Sebastian Bergmann", 604 | "email": "sebastian@phpunit.de", 605 | "role": "lead" 606 | } 607 | ], 608 | "description": "Utility class for timing", 609 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 610 | "keywords": [ 611 | "timer" 612 | ], 613 | "support": { 614 | "issues": "https://github.com/sebastianbergmann/php-timer/issues", 615 | "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" 616 | }, 617 | "funding": [ 618 | { 619 | "url": "https://github.com/sebastianbergmann", 620 | "type": "github" 621 | } 622 | ], 623 | "time": "2023-02-03T06:57:52+00:00" 624 | }, 625 | { 626 | "name": "phpunit/phpunit", 627 | "version": "10.5.46", 628 | "source": { 629 | "type": "git", 630 | "url": "https://github.com/sebastianbergmann/phpunit.git", 631 | "reference": "8080be387a5be380dda48c6f41cee4a13aadab3d" 632 | }, 633 | "dist": { 634 | "type": "zip", 635 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/8080be387a5be380dda48c6f41cee4a13aadab3d", 636 | "reference": "8080be387a5be380dda48c6f41cee4a13aadab3d", 637 | "shasum": "" 638 | }, 639 | "require": { 640 | "ext-dom": "*", 641 | "ext-json": "*", 642 | "ext-libxml": "*", 643 | "ext-mbstring": "*", 644 | "ext-xml": "*", 645 | "ext-xmlwriter": "*", 646 | "myclabs/deep-copy": "^1.13.1", 647 | "phar-io/manifest": "^2.0.4", 648 | "phar-io/version": "^3.2.1", 649 | "php": ">=8.1", 650 | "phpunit/php-code-coverage": "^10.1.16", 651 | "phpunit/php-file-iterator": "^4.1.0", 652 | "phpunit/php-invoker": "^4.0.0", 653 | "phpunit/php-text-template": "^3.0.1", 654 | "phpunit/php-timer": "^6.0.0", 655 | "sebastian/cli-parser": "^2.0.1", 656 | "sebastian/code-unit": "^2.0.0", 657 | "sebastian/comparator": "^5.0.3", 658 | "sebastian/diff": "^5.1.1", 659 | "sebastian/environment": "^6.1.0", 660 | "sebastian/exporter": "^5.1.2", 661 | "sebastian/global-state": "^6.0.2", 662 | "sebastian/object-enumerator": "^5.0.0", 663 | "sebastian/recursion-context": "^5.0.0", 664 | "sebastian/type": "^4.0.0", 665 | "sebastian/version": "^4.0.1" 666 | }, 667 | "suggest": { 668 | "ext-soap": "To be able to generate mocks based on WSDL files" 669 | }, 670 | "bin": [ 671 | "phpunit" 672 | ], 673 | "type": "library", 674 | "extra": { 675 | "branch-alias": { 676 | "dev-main": "10.5-dev" 677 | } 678 | }, 679 | "autoload": { 680 | "files": [ 681 | "src/Framework/Assert/Functions.php" 682 | ], 683 | "classmap": [ 684 | "src/" 685 | ] 686 | }, 687 | "notification-url": "https://packagist.org/downloads/", 688 | "license": [ 689 | "BSD-3-Clause" 690 | ], 691 | "authors": [ 692 | { 693 | "name": "Sebastian Bergmann", 694 | "email": "sebastian@phpunit.de", 695 | "role": "lead" 696 | } 697 | ], 698 | "description": "The PHP Unit Testing framework.", 699 | "homepage": "https://phpunit.de/", 700 | "keywords": [ 701 | "phpunit", 702 | "testing", 703 | "xunit" 704 | ], 705 | "support": { 706 | "issues": "https://github.com/sebastianbergmann/phpunit/issues", 707 | "security": "https://github.com/sebastianbergmann/phpunit/security/policy", 708 | "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.46" 709 | }, 710 | "funding": [ 711 | { 712 | "url": "https://phpunit.de/sponsors.html", 713 | "type": "custom" 714 | }, 715 | { 716 | "url": "https://github.com/sebastianbergmann", 717 | "type": "github" 718 | }, 719 | { 720 | "url": "https://liberapay.com/sebastianbergmann", 721 | "type": "liberapay" 722 | }, 723 | { 724 | "url": "https://thanks.dev/u/gh/sebastianbergmann", 725 | "type": "thanks_dev" 726 | }, 727 | { 728 | "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", 729 | "type": "tidelift" 730 | } 731 | ], 732 | "time": "2025-05-02T06:46:24+00:00" 733 | }, 734 | { 735 | "name": "sebastian/cli-parser", 736 | "version": "2.0.1", 737 | "source": { 738 | "type": "git", 739 | "url": "https://github.com/sebastianbergmann/cli-parser.git", 740 | "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" 741 | }, 742 | "dist": { 743 | "type": "zip", 744 | "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", 745 | "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", 746 | "shasum": "" 747 | }, 748 | "require": { 749 | "php": ">=8.1" 750 | }, 751 | "require-dev": { 752 | "phpunit/phpunit": "^10.0" 753 | }, 754 | "type": "library", 755 | "extra": { 756 | "branch-alias": { 757 | "dev-main": "2.0-dev" 758 | } 759 | }, 760 | "autoload": { 761 | "classmap": [ 762 | "src/" 763 | ] 764 | }, 765 | "notification-url": "https://packagist.org/downloads/", 766 | "license": [ 767 | "BSD-3-Clause" 768 | ], 769 | "authors": [ 770 | { 771 | "name": "Sebastian Bergmann", 772 | "email": "sebastian@phpunit.de", 773 | "role": "lead" 774 | } 775 | ], 776 | "description": "Library for parsing CLI options", 777 | "homepage": "https://github.com/sebastianbergmann/cli-parser", 778 | "support": { 779 | "issues": "https://github.com/sebastianbergmann/cli-parser/issues", 780 | "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", 781 | "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1" 782 | }, 783 | "funding": [ 784 | { 785 | "url": "https://github.com/sebastianbergmann", 786 | "type": "github" 787 | } 788 | ], 789 | "time": "2024-03-02T07:12:49+00:00" 790 | }, 791 | { 792 | "name": "sebastian/code-unit", 793 | "version": "2.0.0", 794 | "source": { 795 | "type": "git", 796 | "url": "https://github.com/sebastianbergmann/code-unit.git", 797 | "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" 798 | }, 799 | "dist": { 800 | "type": "zip", 801 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", 802 | "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", 803 | "shasum": "" 804 | }, 805 | "require": { 806 | "php": ">=8.1" 807 | }, 808 | "require-dev": { 809 | "phpunit/phpunit": "^10.0" 810 | }, 811 | "type": "library", 812 | "extra": { 813 | "branch-alias": { 814 | "dev-main": "2.0-dev" 815 | } 816 | }, 817 | "autoload": { 818 | "classmap": [ 819 | "src/" 820 | ] 821 | }, 822 | "notification-url": "https://packagist.org/downloads/", 823 | "license": [ 824 | "BSD-3-Clause" 825 | ], 826 | "authors": [ 827 | { 828 | "name": "Sebastian Bergmann", 829 | "email": "sebastian@phpunit.de", 830 | "role": "lead" 831 | } 832 | ], 833 | "description": "Collection of value objects that represent the PHP code units", 834 | "homepage": "https://github.com/sebastianbergmann/code-unit", 835 | "support": { 836 | "issues": "https://github.com/sebastianbergmann/code-unit/issues", 837 | "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" 838 | }, 839 | "funding": [ 840 | { 841 | "url": "https://github.com/sebastianbergmann", 842 | "type": "github" 843 | } 844 | ], 845 | "time": "2023-02-03T06:58:43+00:00" 846 | }, 847 | { 848 | "name": "sebastian/code-unit-reverse-lookup", 849 | "version": "3.0.0", 850 | "source": { 851 | "type": "git", 852 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", 853 | "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" 854 | }, 855 | "dist": { 856 | "type": "zip", 857 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", 858 | "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", 859 | "shasum": "" 860 | }, 861 | "require": { 862 | "php": ">=8.1" 863 | }, 864 | "require-dev": { 865 | "phpunit/phpunit": "^10.0" 866 | }, 867 | "type": "library", 868 | "extra": { 869 | "branch-alias": { 870 | "dev-main": "3.0-dev" 871 | } 872 | }, 873 | "autoload": { 874 | "classmap": [ 875 | "src/" 876 | ] 877 | }, 878 | "notification-url": "https://packagist.org/downloads/", 879 | "license": [ 880 | "BSD-3-Clause" 881 | ], 882 | "authors": [ 883 | { 884 | "name": "Sebastian Bergmann", 885 | "email": "sebastian@phpunit.de" 886 | } 887 | ], 888 | "description": "Looks up which function or method a line of code belongs to", 889 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", 890 | "support": { 891 | "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", 892 | "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" 893 | }, 894 | "funding": [ 895 | { 896 | "url": "https://github.com/sebastianbergmann", 897 | "type": "github" 898 | } 899 | ], 900 | "time": "2023-02-03T06:59:15+00:00" 901 | }, 902 | { 903 | "name": "sebastian/comparator", 904 | "version": "5.0.3", 905 | "source": { 906 | "type": "git", 907 | "url": "https://github.com/sebastianbergmann/comparator.git", 908 | "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e" 909 | }, 910 | "dist": { 911 | "type": "zip", 912 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", 913 | "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", 914 | "shasum": "" 915 | }, 916 | "require": { 917 | "ext-dom": "*", 918 | "ext-mbstring": "*", 919 | "php": ">=8.1", 920 | "sebastian/diff": "^5.0", 921 | "sebastian/exporter": "^5.0" 922 | }, 923 | "require-dev": { 924 | "phpunit/phpunit": "^10.5" 925 | }, 926 | "type": "library", 927 | "extra": { 928 | "branch-alias": { 929 | "dev-main": "5.0-dev" 930 | } 931 | }, 932 | "autoload": { 933 | "classmap": [ 934 | "src/" 935 | ] 936 | }, 937 | "notification-url": "https://packagist.org/downloads/", 938 | "license": [ 939 | "BSD-3-Clause" 940 | ], 941 | "authors": [ 942 | { 943 | "name": "Sebastian Bergmann", 944 | "email": "sebastian@phpunit.de" 945 | }, 946 | { 947 | "name": "Jeff Welch", 948 | "email": "whatthejeff@gmail.com" 949 | }, 950 | { 951 | "name": "Volker Dusch", 952 | "email": "github@wallbash.com" 953 | }, 954 | { 955 | "name": "Bernhard Schussek", 956 | "email": "bschussek@2bepublished.at" 957 | } 958 | ], 959 | "description": "Provides the functionality to compare PHP values for equality", 960 | "homepage": "https://github.com/sebastianbergmann/comparator", 961 | "keywords": [ 962 | "comparator", 963 | "compare", 964 | "equality" 965 | ], 966 | "support": { 967 | "issues": "https://github.com/sebastianbergmann/comparator/issues", 968 | "security": "https://github.com/sebastianbergmann/comparator/security/policy", 969 | "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.3" 970 | }, 971 | "funding": [ 972 | { 973 | "url": "https://github.com/sebastianbergmann", 974 | "type": "github" 975 | } 976 | ], 977 | "time": "2024-10-18T14:56:07+00:00" 978 | }, 979 | { 980 | "name": "sebastian/complexity", 981 | "version": "3.2.0", 982 | "source": { 983 | "type": "git", 984 | "url": "https://github.com/sebastianbergmann/complexity.git", 985 | "reference": "68ff824baeae169ec9f2137158ee529584553799" 986 | }, 987 | "dist": { 988 | "type": "zip", 989 | "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", 990 | "reference": "68ff824baeae169ec9f2137158ee529584553799", 991 | "shasum": "" 992 | }, 993 | "require": { 994 | "nikic/php-parser": "^4.18 || ^5.0", 995 | "php": ">=8.1" 996 | }, 997 | "require-dev": { 998 | "phpunit/phpunit": "^10.0" 999 | }, 1000 | "type": "library", 1001 | "extra": { 1002 | "branch-alias": { 1003 | "dev-main": "3.2-dev" 1004 | } 1005 | }, 1006 | "autoload": { 1007 | "classmap": [ 1008 | "src/" 1009 | ] 1010 | }, 1011 | "notification-url": "https://packagist.org/downloads/", 1012 | "license": [ 1013 | "BSD-3-Clause" 1014 | ], 1015 | "authors": [ 1016 | { 1017 | "name": "Sebastian Bergmann", 1018 | "email": "sebastian@phpunit.de", 1019 | "role": "lead" 1020 | } 1021 | ], 1022 | "description": "Library for calculating the complexity of PHP code units", 1023 | "homepage": "https://github.com/sebastianbergmann/complexity", 1024 | "support": { 1025 | "issues": "https://github.com/sebastianbergmann/complexity/issues", 1026 | "security": "https://github.com/sebastianbergmann/complexity/security/policy", 1027 | "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" 1028 | }, 1029 | "funding": [ 1030 | { 1031 | "url": "https://github.com/sebastianbergmann", 1032 | "type": "github" 1033 | } 1034 | ], 1035 | "time": "2023-12-21T08:37:17+00:00" 1036 | }, 1037 | { 1038 | "name": "sebastian/diff", 1039 | "version": "5.1.1", 1040 | "source": { 1041 | "type": "git", 1042 | "url": "https://github.com/sebastianbergmann/diff.git", 1043 | "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" 1044 | }, 1045 | "dist": { 1046 | "type": "zip", 1047 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", 1048 | "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", 1049 | "shasum": "" 1050 | }, 1051 | "require": { 1052 | "php": ">=8.1" 1053 | }, 1054 | "require-dev": { 1055 | "phpunit/phpunit": "^10.0", 1056 | "symfony/process": "^6.4" 1057 | }, 1058 | "type": "library", 1059 | "extra": { 1060 | "branch-alias": { 1061 | "dev-main": "5.1-dev" 1062 | } 1063 | }, 1064 | "autoload": { 1065 | "classmap": [ 1066 | "src/" 1067 | ] 1068 | }, 1069 | "notification-url": "https://packagist.org/downloads/", 1070 | "license": [ 1071 | "BSD-3-Clause" 1072 | ], 1073 | "authors": [ 1074 | { 1075 | "name": "Sebastian Bergmann", 1076 | "email": "sebastian@phpunit.de" 1077 | }, 1078 | { 1079 | "name": "Kore Nordmann", 1080 | "email": "mail@kore-nordmann.de" 1081 | } 1082 | ], 1083 | "description": "Diff implementation", 1084 | "homepage": "https://github.com/sebastianbergmann/diff", 1085 | "keywords": [ 1086 | "diff", 1087 | "udiff", 1088 | "unidiff", 1089 | "unified diff" 1090 | ], 1091 | "support": { 1092 | "issues": "https://github.com/sebastianbergmann/diff/issues", 1093 | "security": "https://github.com/sebastianbergmann/diff/security/policy", 1094 | "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" 1095 | }, 1096 | "funding": [ 1097 | { 1098 | "url": "https://github.com/sebastianbergmann", 1099 | "type": "github" 1100 | } 1101 | ], 1102 | "time": "2024-03-02T07:15:17+00:00" 1103 | }, 1104 | { 1105 | "name": "sebastian/environment", 1106 | "version": "6.1.0", 1107 | "source": { 1108 | "type": "git", 1109 | "url": "https://github.com/sebastianbergmann/environment.git", 1110 | "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" 1111 | }, 1112 | "dist": { 1113 | "type": "zip", 1114 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", 1115 | "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", 1116 | "shasum": "" 1117 | }, 1118 | "require": { 1119 | "php": ">=8.1" 1120 | }, 1121 | "require-dev": { 1122 | "phpunit/phpunit": "^10.0" 1123 | }, 1124 | "suggest": { 1125 | "ext-posix": "*" 1126 | }, 1127 | "type": "library", 1128 | "extra": { 1129 | "branch-alias": { 1130 | "dev-main": "6.1-dev" 1131 | } 1132 | }, 1133 | "autoload": { 1134 | "classmap": [ 1135 | "src/" 1136 | ] 1137 | }, 1138 | "notification-url": "https://packagist.org/downloads/", 1139 | "license": [ 1140 | "BSD-3-Clause" 1141 | ], 1142 | "authors": [ 1143 | { 1144 | "name": "Sebastian Bergmann", 1145 | "email": "sebastian@phpunit.de" 1146 | } 1147 | ], 1148 | "description": "Provides functionality to handle HHVM/PHP environments", 1149 | "homepage": "https://github.com/sebastianbergmann/environment", 1150 | "keywords": [ 1151 | "Xdebug", 1152 | "environment", 1153 | "hhvm" 1154 | ], 1155 | "support": { 1156 | "issues": "https://github.com/sebastianbergmann/environment/issues", 1157 | "security": "https://github.com/sebastianbergmann/environment/security/policy", 1158 | "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" 1159 | }, 1160 | "funding": [ 1161 | { 1162 | "url": "https://github.com/sebastianbergmann", 1163 | "type": "github" 1164 | } 1165 | ], 1166 | "time": "2024-03-23T08:47:14+00:00" 1167 | }, 1168 | { 1169 | "name": "sebastian/exporter", 1170 | "version": "5.1.2", 1171 | "source": { 1172 | "type": "git", 1173 | "url": "https://github.com/sebastianbergmann/exporter.git", 1174 | "reference": "955288482d97c19a372d3f31006ab3f37da47adf" 1175 | }, 1176 | "dist": { 1177 | "type": "zip", 1178 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf", 1179 | "reference": "955288482d97c19a372d3f31006ab3f37da47adf", 1180 | "shasum": "" 1181 | }, 1182 | "require": { 1183 | "ext-mbstring": "*", 1184 | "php": ">=8.1", 1185 | "sebastian/recursion-context": "^5.0" 1186 | }, 1187 | "require-dev": { 1188 | "phpunit/phpunit": "^10.0" 1189 | }, 1190 | "type": "library", 1191 | "extra": { 1192 | "branch-alias": { 1193 | "dev-main": "5.1-dev" 1194 | } 1195 | }, 1196 | "autoload": { 1197 | "classmap": [ 1198 | "src/" 1199 | ] 1200 | }, 1201 | "notification-url": "https://packagist.org/downloads/", 1202 | "license": [ 1203 | "BSD-3-Clause" 1204 | ], 1205 | "authors": [ 1206 | { 1207 | "name": "Sebastian Bergmann", 1208 | "email": "sebastian@phpunit.de" 1209 | }, 1210 | { 1211 | "name": "Jeff Welch", 1212 | "email": "whatthejeff@gmail.com" 1213 | }, 1214 | { 1215 | "name": "Volker Dusch", 1216 | "email": "github@wallbash.com" 1217 | }, 1218 | { 1219 | "name": "Adam Harvey", 1220 | "email": "aharvey@php.net" 1221 | }, 1222 | { 1223 | "name": "Bernhard Schussek", 1224 | "email": "bschussek@gmail.com" 1225 | } 1226 | ], 1227 | "description": "Provides the functionality to export PHP variables for visualization", 1228 | "homepage": "https://www.github.com/sebastianbergmann/exporter", 1229 | "keywords": [ 1230 | "export", 1231 | "exporter" 1232 | ], 1233 | "support": { 1234 | "issues": "https://github.com/sebastianbergmann/exporter/issues", 1235 | "security": "https://github.com/sebastianbergmann/exporter/security/policy", 1236 | "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2" 1237 | }, 1238 | "funding": [ 1239 | { 1240 | "url": "https://github.com/sebastianbergmann", 1241 | "type": "github" 1242 | } 1243 | ], 1244 | "time": "2024-03-02T07:17:12+00:00" 1245 | }, 1246 | { 1247 | "name": "sebastian/global-state", 1248 | "version": "6.0.2", 1249 | "source": { 1250 | "type": "git", 1251 | "url": "https://github.com/sebastianbergmann/global-state.git", 1252 | "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" 1253 | }, 1254 | "dist": { 1255 | "type": "zip", 1256 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", 1257 | "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", 1258 | "shasum": "" 1259 | }, 1260 | "require": { 1261 | "php": ">=8.1", 1262 | "sebastian/object-reflector": "^3.0", 1263 | "sebastian/recursion-context": "^5.0" 1264 | }, 1265 | "require-dev": { 1266 | "ext-dom": "*", 1267 | "phpunit/phpunit": "^10.0" 1268 | }, 1269 | "type": "library", 1270 | "extra": { 1271 | "branch-alias": { 1272 | "dev-main": "6.0-dev" 1273 | } 1274 | }, 1275 | "autoload": { 1276 | "classmap": [ 1277 | "src/" 1278 | ] 1279 | }, 1280 | "notification-url": "https://packagist.org/downloads/", 1281 | "license": [ 1282 | "BSD-3-Clause" 1283 | ], 1284 | "authors": [ 1285 | { 1286 | "name": "Sebastian Bergmann", 1287 | "email": "sebastian@phpunit.de" 1288 | } 1289 | ], 1290 | "description": "Snapshotting of global state", 1291 | "homepage": "https://www.github.com/sebastianbergmann/global-state", 1292 | "keywords": [ 1293 | "global state" 1294 | ], 1295 | "support": { 1296 | "issues": "https://github.com/sebastianbergmann/global-state/issues", 1297 | "security": "https://github.com/sebastianbergmann/global-state/security/policy", 1298 | "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" 1299 | }, 1300 | "funding": [ 1301 | { 1302 | "url": "https://github.com/sebastianbergmann", 1303 | "type": "github" 1304 | } 1305 | ], 1306 | "time": "2024-03-02T07:19:19+00:00" 1307 | }, 1308 | { 1309 | "name": "sebastian/lines-of-code", 1310 | "version": "2.0.2", 1311 | "source": { 1312 | "type": "git", 1313 | "url": "https://github.com/sebastianbergmann/lines-of-code.git", 1314 | "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" 1315 | }, 1316 | "dist": { 1317 | "type": "zip", 1318 | "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", 1319 | "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", 1320 | "shasum": "" 1321 | }, 1322 | "require": { 1323 | "nikic/php-parser": "^4.18 || ^5.0", 1324 | "php": ">=8.1" 1325 | }, 1326 | "require-dev": { 1327 | "phpunit/phpunit": "^10.0" 1328 | }, 1329 | "type": "library", 1330 | "extra": { 1331 | "branch-alias": { 1332 | "dev-main": "2.0-dev" 1333 | } 1334 | }, 1335 | "autoload": { 1336 | "classmap": [ 1337 | "src/" 1338 | ] 1339 | }, 1340 | "notification-url": "https://packagist.org/downloads/", 1341 | "license": [ 1342 | "BSD-3-Clause" 1343 | ], 1344 | "authors": [ 1345 | { 1346 | "name": "Sebastian Bergmann", 1347 | "email": "sebastian@phpunit.de", 1348 | "role": "lead" 1349 | } 1350 | ], 1351 | "description": "Library for counting the lines of code in PHP source code", 1352 | "homepage": "https://github.com/sebastianbergmann/lines-of-code", 1353 | "support": { 1354 | "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", 1355 | "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", 1356 | "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" 1357 | }, 1358 | "funding": [ 1359 | { 1360 | "url": "https://github.com/sebastianbergmann", 1361 | "type": "github" 1362 | } 1363 | ], 1364 | "time": "2023-12-21T08:38:20+00:00" 1365 | }, 1366 | { 1367 | "name": "sebastian/object-enumerator", 1368 | "version": "5.0.0", 1369 | "source": { 1370 | "type": "git", 1371 | "url": "https://github.com/sebastianbergmann/object-enumerator.git", 1372 | "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" 1373 | }, 1374 | "dist": { 1375 | "type": "zip", 1376 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", 1377 | "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", 1378 | "shasum": "" 1379 | }, 1380 | "require": { 1381 | "php": ">=8.1", 1382 | "sebastian/object-reflector": "^3.0", 1383 | "sebastian/recursion-context": "^5.0" 1384 | }, 1385 | "require-dev": { 1386 | "phpunit/phpunit": "^10.0" 1387 | }, 1388 | "type": "library", 1389 | "extra": { 1390 | "branch-alias": { 1391 | "dev-main": "5.0-dev" 1392 | } 1393 | }, 1394 | "autoload": { 1395 | "classmap": [ 1396 | "src/" 1397 | ] 1398 | }, 1399 | "notification-url": "https://packagist.org/downloads/", 1400 | "license": [ 1401 | "BSD-3-Clause" 1402 | ], 1403 | "authors": [ 1404 | { 1405 | "name": "Sebastian Bergmann", 1406 | "email": "sebastian@phpunit.de" 1407 | } 1408 | ], 1409 | "description": "Traverses array structures and object graphs to enumerate all referenced objects", 1410 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/", 1411 | "support": { 1412 | "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", 1413 | "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" 1414 | }, 1415 | "funding": [ 1416 | { 1417 | "url": "https://github.com/sebastianbergmann", 1418 | "type": "github" 1419 | } 1420 | ], 1421 | "time": "2023-02-03T07:08:32+00:00" 1422 | }, 1423 | { 1424 | "name": "sebastian/object-reflector", 1425 | "version": "3.0.0", 1426 | "source": { 1427 | "type": "git", 1428 | "url": "https://github.com/sebastianbergmann/object-reflector.git", 1429 | "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" 1430 | }, 1431 | "dist": { 1432 | "type": "zip", 1433 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", 1434 | "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", 1435 | "shasum": "" 1436 | }, 1437 | "require": { 1438 | "php": ">=8.1" 1439 | }, 1440 | "require-dev": { 1441 | "phpunit/phpunit": "^10.0" 1442 | }, 1443 | "type": "library", 1444 | "extra": { 1445 | "branch-alias": { 1446 | "dev-main": "3.0-dev" 1447 | } 1448 | }, 1449 | "autoload": { 1450 | "classmap": [ 1451 | "src/" 1452 | ] 1453 | }, 1454 | "notification-url": "https://packagist.org/downloads/", 1455 | "license": [ 1456 | "BSD-3-Clause" 1457 | ], 1458 | "authors": [ 1459 | { 1460 | "name": "Sebastian Bergmann", 1461 | "email": "sebastian@phpunit.de" 1462 | } 1463 | ], 1464 | "description": "Allows reflection of object attributes, including inherited and non-public ones", 1465 | "homepage": "https://github.com/sebastianbergmann/object-reflector/", 1466 | "support": { 1467 | "issues": "https://github.com/sebastianbergmann/object-reflector/issues", 1468 | "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" 1469 | }, 1470 | "funding": [ 1471 | { 1472 | "url": "https://github.com/sebastianbergmann", 1473 | "type": "github" 1474 | } 1475 | ], 1476 | "time": "2023-02-03T07:06:18+00:00" 1477 | }, 1478 | { 1479 | "name": "sebastian/recursion-context", 1480 | "version": "5.0.0", 1481 | "source": { 1482 | "type": "git", 1483 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1484 | "reference": "05909fb5bc7df4c52992396d0116aed689f93712" 1485 | }, 1486 | "dist": { 1487 | "type": "zip", 1488 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", 1489 | "reference": "05909fb5bc7df4c52992396d0116aed689f93712", 1490 | "shasum": "" 1491 | }, 1492 | "require": { 1493 | "php": ">=8.1" 1494 | }, 1495 | "require-dev": { 1496 | "phpunit/phpunit": "^10.0" 1497 | }, 1498 | "type": "library", 1499 | "extra": { 1500 | "branch-alias": { 1501 | "dev-main": "5.0-dev" 1502 | } 1503 | }, 1504 | "autoload": { 1505 | "classmap": [ 1506 | "src/" 1507 | ] 1508 | }, 1509 | "notification-url": "https://packagist.org/downloads/", 1510 | "license": [ 1511 | "BSD-3-Clause" 1512 | ], 1513 | "authors": [ 1514 | { 1515 | "name": "Sebastian Bergmann", 1516 | "email": "sebastian@phpunit.de" 1517 | }, 1518 | { 1519 | "name": "Jeff Welch", 1520 | "email": "whatthejeff@gmail.com" 1521 | }, 1522 | { 1523 | "name": "Adam Harvey", 1524 | "email": "aharvey@php.net" 1525 | } 1526 | ], 1527 | "description": "Provides functionality to recursively process PHP variables", 1528 | "homepage": "https://github.com/sebastianbergmann/recursion-context", 1529 | "support": { 1530 | "issues": "https://github.com/sebastianbergmann/recursion-context/issues", 1531 | "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" 1532 | }, 1533 | "funding": [ 1534 | { 1535 | "url": "https://github.com/sebastianbergmann", 1536 | "type": "github" 1537 | } 1538 | ], 1539 | "time": "2023-02-03T07:05:40+00:00" 1540 | }, 1541 | { 1542 | "name": "sebastian/type", 1543 | "version": "4.0.0", 1544 | "source": { 1545 | "type": "git", 1546 | "url": "https://github.com/sebastianbergmann/type.git", 1547 | "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" 1548 | }, 1549 | "dist": { 1550 | "type": "zip", 1551 | "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", 1552 | "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", 1553 | "shasum": "" 1554 | }, 1555 | "require": { 1556 | "php": ">=8.1" 1557 | }, 1558 | "require-dev": { 1559 | "phpunit/phpunit": "^10.0" 1560 | }, 1561 | "type": "library", 1562 | "extra": { 1563 | "branch-alias": { 1564 | "dev-main": "4.0-dev" 1565 | } 1566 | }, 1567 | "autoload": { 1568 | "classmap": [ 1569 | "src/" 1570 | ] 1571 | }, 1572 | "notification-url": "https://packagist.org/downloads/", 1573 | "license": [ 1574 | "BSD-3-Clause" 1575 | ], 1576 | "authors": [ 1577 | { 1578 | "name": "Sebastian Bergmann", 1579 | "email": "sebastian@phpunit.de", 1580 | "role": "lead" 1581 | } 1582 | ], 1583 | "description": "Collection of value objects that represent the types of the PHP type system", 1584 | "homepage": "https://github.com/sebastianbergmann/type", 1585 | "support": { 1586 | "issues": "https://github.com/sebastianbergmann/type/issues", 1587 | "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" 1588 | }, 1589 | "funding": [ 1590 | { 1591 | "url": "https://github.com/sebastianbergmann", 1592 | "type": "github" 1593 | } 1594 | ], 1595 | "time": "2023-02-03T07:10:45+00:00" 1596 | }, 1597 | { 1598 | "name": "sebastian/version", 1599 | "version": "4.0.1", 1600 | "source": { 1601 | "type": "git", 1602 | "url": "https://github.com/sebastianbergmann/version.git", 1603 | "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" 1604 | }, 1605 | "dist": { 1606 | "type": "zip", 1607 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", 1608 | "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", 1609 | "shasum": "" 1610 | }, 1611 | "require": { 1612 | "php": ">=8.1" 1613 | }, 1614 | "type": "library", 1615 | "extra": { 1616 | "branch-alias": { 1617 | "dev-main": "4.0-dev" 1618 | } 1619 | }, 1620 | "autoload": { 1621 | "classmap": [ 1622 | "src/" 1623 | ] 1624 | }, 1625 | "notification-url": "https://packagist.org/downloads/", 1626 | "license": [ 1627 | "BSD-3-Clause" 1628 | ], 1629 | "authors": [ 1630 | { 1631 | "name": "Sebastian Bergmann", 1632 | "email": "sebastian@phpunit.de", 1633 | "role": "lead" 1634 | } 1635 | ], 1636 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1637 | "homepage": "https://github.com/sebastianbergmann/version", 1638 | "support": { 1639 | "issues": "https://github.com/sebastianbergmann/version/issues", 1640 | "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" 1641 | }, 1642 | "funding": [ 1643 | { 1644 | "url": "https://github.com/sebastianbergmann", 1645 | "type": "github" 1646 | } 1647 | ], 1648 | "time": "2023-02-07T11:34:05+00:00" 1649 | }, 1650 | { 1651 | "name": "theseer/tokenizer", 1652 | "version": "1.2.3", 1653 | "source": { 1654 | "type": "git", 1655 | "url": "https://github.com/theseer/tokenizer.git", 1656 | "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" 1657 | }, 1658 | "dist": { 1659 | "type": "zip", 1660 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", 1661 | "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", 1662 | "shasum": "" 1663 | }, 1664 | "require": { 1665 | "ext-dom": "*", 1666 | "ext-tokenizer": "*", 1667 | "ext-xmlwriter": "*", 1668 | "php": "^7.2 || ^8.0" 1669 | }, 1670 | "type": "library", 1671 | "autoload": { 1672 | "classmap": [ 1673 | "src/" 1674 | ] 1675 | }, 1676 | "notification-url": "https://packagist.org/downloads/", 1677 | "license": [ 1678 | "BSD-3-Clause" 1679 | ], 1680 | "authors": [ 1681 | { 1682 | "name": "Arne Blankerts", 1683 | "email": "arne@blankerts.de", 1684 | "role": "Developer" 1685 | } 1686 | ], 1687 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", 1688 | "support": { 1689 | "issues": "https://github.com/theseer/tokenizer/issues", 1690 | "source": "https://github.com/theseer/tokenizer/tree/1.2.3" 1691 | }, 1692 | "funding": [ 1693 | { 1694 | "url": "https://github.com/theseer", 1695 | "type": "github" 1696 | } 1697 | ], 1698 | "time": "2024-03-03T12:36:25+00:00" 1699 | } 1700 | ], 1701 | "aliases": [], 1702 | "minimum-stability": "stable", 1703 | "stability-flags": {}, 1704 | "prefer-stable": false, 1705 | "prefer-lowest": false, 1706 | "platform": { 1707 | "php": "^8.1.0", 1708 | "ext-ffi": "*" 1709 | }, 1710 | "platform-dev": { 1711 | "ext-iconv": "*" 1712 | }, 1713 | "plugin-api-version": "2.6.0" 1714 | } 1715 | -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: 7 3 | paths: 4 | - src 5 | - tests 6 | checkMissingIterableValueType: false 7 | ignoreErrors: 8 | - '/Call to an undefined method FFI::sb_stemmer_new\(\)/' 9 | - '/Call to an undefined method FFI::sb_stemmer_list\(\)/' 10 | - '/Call to an undefined method FFI::sb_stemmer_delete\(\)/' 11 | - '/Call to an undefined method FFI::sb_stemmer_stem\(\)/' 12 | - '/Call to an undefined method FFI::sb_stemmer_length\(\)/' -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | 16 | tests 17 | 18 | 19 | 20 | 21 | ./src/ 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /resources/libstemmer.h: -------------------------------------------------------------------------------- 1 | #define FFI_LIB "libstemmer.so" 2 | 3 | struct sb_stemmer; 4 | typedef unsigned char sb_symbol; 5 | 6 | /** Returns an array of the names of the available stemming algorithms. 7 | * Note that these are the canonical names - aliases (ie, other names for 8 | * the same algorithm) will not be included in the list. 9 | * The list is terminated with a null pointer. 10 | * 11 | * The list must not be modified in any way. 12 | */ 13 | const char ** sb_stemmer_list(void); 14 | 15 | /** Create a new stemmer object, using the specified algorithm, for the 16 | * specified character encoding. 17 | * 18 | * All algorithms will usually be available in UTF-8, but may also be 19 | * available in other character encodings. 20 | * 21 | * @param algorithm The algorithm name. This is either the english 22 | * name of the algorithm, or the 2 or 3 letter ISO 639 codes for the 23 | * language. Note that case is significant in this parameter - the 24 | * value should be supplied in lower case. 25 | * 26 | * @param charenc The character encoding. NULL may be passed as 27 | * this value, in which case UTF-8 encoding will be assumed. Otherwise, 28 | * the argument may be one of "UTF_8", "ISO_8859_1" (i.e. Latin 1), 29 | * "ISO_8859_2" (i.e. Latin 2) or "KOI8_R" (Russian). Note that case is 30 | * significant in this parameter. 31 | * 32 | * @return NULL if the specified algorithm is not recognised, or the 33 | * algorithm is not available for the requested encoding. Otherwise, 34 | * returns a pointer to a newly created stemmer for the requested algorithm. 35 | * The returned pointer must be deleted by calling sb_stemmer_delete(). 36 | * 37 | * @note NULL will also be returned if an out of memory error occurs. 38 | */ 39 | struct sb_stemmer * sb_stemmer_new(const char * algorithm, const char * charenc); 40 | 41 | /** Delete a stemmer object. 42 | * 43 | * This frees all resources allocated for the stemmer. After calling 44 | * this function, the supplied stemmer may no longer be used in any way. 45 | * 46 | * It is safe to pass a null pointer to this function - this will have 47 | * no effect. 48 | */ 49 | void sb_stemmer_delete(struct sb_stemmer * stemmer); 50 | 51 | /** Stem a word. 52 | * 53 | * The return value is owned by the stemmer - it must not be freed or 54 | * modified, and it will become invalid when the stemmer is called again, 55 | * or if the stemmer is freed. 56 | * 57 | * The length of the return value can be obtained using sb_stemmer_length(). 58 | * 59 | * If an out-of-memory error occurs, this will return NULL. 60 | */ 61 | const sb_symbol * sb_stemmer_stem(struct sb_stemmer * stemmer, const sb_symbol * word, int size); 62 | 63 | /** Get the length of the result of the last stemmed word. 64 | * This should not be called before sb_stemmer_stem() has been called. 65 | */ 66 | int sb_stemmer_length(struct sb_stemmer * stemmer); 67 | -------------------------------------------------------------------------------- /src/Adapter/Libstemmer.php: -------------------------------------------------------------------------------- 1 | ffi = \FFI::load($filename); 20 | 21 | } 22 | 23 | /** 24 | * @throws UnavailableAlgorithmException 25 | */ 26 | public function sbStemmerNew(string $algorithm, CharacterEncodingEnum $charenc): CData 27 | { 28 | 29 | $sbStemmer = $this->ffi->sb_stemmer_new($algorithm, $charenc->name); 30 | if (is_null($sbStemmer)) { 31 | throw new UnavailableAlgorithmException(); 32 | } 33 | return $sbStemmer; 34 | 35 | } 36 | 37 | public function sbStemmerList(): CData 38 | { 39 | 40 | return $this->ffi->sb_stemmer_list(); 41 | 42 | } 43 | 44 | public function sbStemmerDelete(CData $sbStemmer): void 45 | { 46 | 47 | $this->ffi->sb_stemmer_delete($sbStemmer); 48 | 49 | } 50 | 51 | public function sbStemmerStem(CData $sbStemmer, string $word, int $size): CData 52 | { 53 | 54 | $c_word = $this->ffi->new("char[$size]"); 55 | FFI::memcpy($c_word, $word, $size); 56 | $sb_symbol = $this->ffi->cast($this->ffi->type('sb_symbol'), $c_word); 57 | $word = FFI::addr($sb_symbol); 58 | 59 | return $this->ffi->sb_stemmer_stem($sbStemmer, $word, $size); 60 | 61 | } 62 | 63 | public function sbStemmerLength(CData $sbStemmer): int 64 | { 65 | 66 | return $this->ffi->sb_stemmer_length($sbStemmer); 67 | 68 | } 69 | 70 | } -------------------------------------------------------------------------------- /src/Enum/CharacterEncodingEnum.php: -------------------------------------------------------------------------------- 1 | libstemmer = new Libstemmer(self::HEADER_PATH); 30 | $this->stemmer = $this->libstemmer->sbStemmerNew($algorithm, $charenc); 31 | 32 | } 33 | 34 | public static function algorithms(): array 35 | { 36 | 37 | $libstemmer = new Libstemmer(self::HEADER_PATH); 38 | /** @var array $stemmerList */ 39 | $stemmerList = $libstemmer->sbStemmerList(); 40 | $algorithms = []; 41 | $i = 0; 42 | while ($stemmerList[$i] != NULL) { 43 | $algorithms[] = $stemmerList[$i]; 44 | $i++; 45 | } 46 | 47 | return $algorithms; 48 | 49 | } 50 | 51 | public function stemWord(string $word): string 52 | { 53 | 54 | $size = strlen($word); 55 | $stem = $this->libstemmer->sbStemmerStem($this->stemmer, $word, $size); 56 | $size = $this->libstemmer->sbStemmerLength($this->stemmer); 57 | 58 | return FFI::string($stem, $size); 59 | 60 | } 61 | 62 | public function stemWords(array $words): array 63 | { 64 | 65 | $stems = []; 66 | foreach ($words as $word) { 67 | $stems[] = $this->stemWord($word); 68 | } 69 | 70 | return $stems; 71 | 72 | } 73 | 74 | } -------------------------------------------------------------------------------- /src/StemmerInterface.php: -------------------------------------------------------------------------------- 1 | expectException(Exception::class); 17 | $filename = "filethatdoesnotexist.h"; 18 | new Libstemmer($filename); 19 | 20 | } 21 | 22 | } 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /tests/StemmerTest.php: -------------------------------------------------------------------------------- 1 | assertIsArray($algorithms); 19 | $this->assertNotEmpty($algorithms); 20 | 21 | } 22 | 23 | /** 24 | * @throws UnavailableAlgorithmException 25 | */ 26 | public function testThatStemmerWithUnavailableAlgorithmThrowsException(): void 27 | { 28 | 29 | $this->expectException(UnavailableAlgorithmException::class); 30 | $algorithm = "dothraki"; 31 | new Stemmer($algorithm); 32 | 33 | } 34 | 35 | public function testThatStemmerWithAvailableAlgorithmAndUnavailableEncodingThrowsException(): void 36 | { 37 | 38 | $this->expectException(UnavailableAlgorithmException::class); 39 | $algorithm = "italian"; 40 | new Stemmer($algorithm, CharacterEncodingEnum::KOI8_R); 41 | 42 | } 43 | 44 | /** 45 | * @param string $algorithm 46 | * @param string $word 47 | * @param string $stem 48 | * 49 | * @dataProvider stemWordUtf8CharencProvider 50 | * @throws UnavailableAlgorithmException 51 | */ 52 | public function testThatStemWordWithNoCharencReturnsTheExpectedStem(string $algorithm, string $word, string $stem): void 53 | { 54 | 55 | $stemmer = new Stemmer($algorithm); 56 | $this->assertEquals($stem, $stemmer->stemWord($word)); 57 | 58 | } 59 | 60 | /** 61 | * @param string $algorithm 62 | * @param string $word 63 | * @param string $stem 64 | * 65 | * @dataProvider stemWordUtf8CharencProvider 66 | * @throws UnavailableAlgorithmException 67 | */ 68 | public function testThatStemWordWithUtf8CharencReturnsTheExpectedStem(string $algorithm, string $word, string $stem): void 69 | { 70 | 71 | $stemmer = new Stemmer($algorithm, CharacterEncodingEnum::UTF_8); 72 | $this->assertEquals($stem, $stemmer->stemWord($word)); 73 | 74 | } 75 | 76 | public static function stemWordUtf8CharencProvider(): array 77 | { 78 | 79 | return [ 80 | ['english', 'cycling', 'cycl'], 81 | ['italian', 'camminare', 'cammin'], 82 | ['portuguese', 'atribuição', 'atribuiçã'], 83 | ['basque', 'aberatsenetakoa', 'aberatse'], 84 | ['catalan', 'arruïnada', 'arru'], 85 | ['danish', 'afbildningerne', 'afbildning'], 86 | ['hungarian', 'lenyűgözőnek', 'lenyűgöző'], 87 | ['romanian', 'luminișurile', 'luminișur'], 88 | ['russian', 'взъерошенный', 'взъерошен'] 89 | ]; 90 | 91 | } 92 | 93 | /** 94 | * @param string $algorithm 95 | * @param string $word 96 | * @param string $stem 97 | * 98 | * @dataProvider stemWordIso88591CharencProvider 99 | * @throws UnavailableAlgorithmException 100 | */ 101 | public function testThatStemWordWithIso88591CharencReturnsTheExpectedStem(string $algorithm, string $word, string $stem): void 102 | { 103 | 104 | /** @var string $word */ 105 | $word = iconv('UTF-8', 'ISO-8859-1', $word); 106 | $stemmer = new Stemmer($algorithm, CharacterEncodingEnum::ISO_8859_1); 107 | $actualStem = iconv('ISO-8859-1', 'UTF-8', $stemmer->stemWord($word)); 108 | $this->assertEquals($stem, $actualStem); 109 | 110 | } 111 | 112 | public static function stemWordIso88591CharencProvider(): array 113 | { 114 | 115 | return [ 116 | ['basque', 'aberatsenetakoa', 'aberatse'], 117 | ['catalan', 'arruïnada', 'arru'], 118 | ['danish', 'afbildningerne', 'afbildning'], 119 | ]; 120 | 121 | } 122 | 123 | /** 124 | * @param string $algorithm 125 | * @param string $word 126 | * @param string $stem 127 | * 128 | * @dataProvider stemWordIso88592CharencProvider 129 | * @throws UnavailableAlgorithmException 130 | */ 131 | public function testThatStemWordWithIso88592CharencReturnsTheExpectedStem(string $algorithm, string $word, string $stem): void 132 | { 133 | 134 | /** @var string $word */ 135 | $word = iconv('UTF-8', 'ISO-8859-2', $word); 136 | $stemmer = new Stemmer($algorithm, CharacterEncodingEnum::ISO_8859_2); 137 | $actualStem = iconv('ISO-8859-2', 'UTF-8', $stemmer->stemWord($word)); 138 | $this->assertEquals($stem, $actualStem); 139 | 140 | } 141 | 142 | public static function stemWordIso88592CharencProvider(): array 143 | { 144 | 145 | return [ 146 | ['hungarian', 'lenyűgözőnek', 'lenyűgöző'], 147 | ]; 148 | 149 | } 150 | 151 | /** 152 | * @param string $algorithm 153 | * @param string $word 154 | * @param string $stem 155 | * 156 | * @dataProvider stemWordKoi8rCharencProvider 157 | * @throws UnavailableAlgorithmException 158 | */ 159 | public function testThatStemWordWithKoi8rCharencReturnsTheExpectedStem(string $algorithm, string $word, string $stem): void 160 | { 161 | 162 | /** @var string $word */ 163 | $word = iconv('UTF-8', 'KOI8-R', $word); 164 | $stemmer = new Stemmer($algorithm, CharacterEncodingEnum::KOI8_R); 165 | $actualStem = iconv('KOI8-R', 'UTF-8', $stemmer->stemWord($word)); 166 | $this->assertEquals($stem, $actualStem); 167 | 168 | } 169 | 170 | public static function stemWordKoi8rCharencProvider(): array 171 | { 172 | 173 | return [ 174 | ['russian', 'взъерошенный', 'взъерошен'], 175 | ]; 176 | 177 | } 178 | 179 | /** 180 | * @param string $algorithm 181 | * @param array $words 182 | * @param array $stems 183 | * 184 | * @dataProvider stemWordsUtf8CharencProvider 185 | * @throws UnavailableAlgorithmException 186 | */ 187 | public function testThatStemWordsWithNoCharencReturnsTheExpectedStems(string $algorithm, array $words, array $stems): void 188 | { 189 | 190 | $stemmer = new Stemmer($algorithm); 191 | $this->assertEquals($stems, $stemmer->stemWords($words)); 192 | 193 | } 194 | 195 | /** 196 | * @param string $algorithm 197 | * @param array $words 198 | * @param array $stems 199 | * 200 | * @dataProvider stemWordsUtf8CharencProvider 201 | * @throws UnavailableAlgorithmException 202 | */ 203 | public function testThatStemWordsWithUtf8CharencReturnsTheExpectedStems(string $algorithm, array $words, array $stems): void 204 | { 205 | 206 | $stemmer = new Stemmer($algorithm, CharacterEncodingEnum::UTF_8); 207 | $this->assertEquals($stems, $stemmer->stemWords($words)); 208 | 209 | } 210 | 211 | public static function stemWordsUtf8CharencProvider(): array 212 | { 213 | 214 | return [ 215 | ['english', ['cycling', 'doors'], ['cycl', 'door']], 216 | ['italian', ['camminare', 'porte'], ['cammin', 'port']], 217 | ['portuguese', ['atribuição', 'obrigações'], ['atribuiçã', 'obrig']], 218 | ['basque', ['aberatsenetakoa', 'txotxongilo'], ['aberatse', 'txotxongilo']], 219 | ['catalan', ['gratuïtament', 'cuaespinós'], ['gratuit', 'cuaespin']], 220 | ['danish', ['afbildningerne', 'linnedklæderne'], ['afbildning', 'linnedklæd']], 221 | ['hungarian', ['lenyűgözőnek', 'megháromszorozódott'], ['lenyűgöző', 'megháromszorozódot']], 222 | ['romanian', ['luminișurile', 'personalităţilor'], ['luminișur', 'personal']], 223 | ['russian', ['взъерошенный', 'затруднительное'], ['взъерошен', 'затруднительн']], 224 | ]; 225 | 226 | } 227 | 228 | /** 229 | * @param string $algorithm 230 | * @param array $words 231 | * @param array $stems 232 | * 233 | * @dataProvider stemWordsIso88591CharencProvider 234 | * @throws UnavailableAlgorithmException 235 | */ 236 | public function testThatStemWordsWithIso88591CharencReturnsTheExpectedStems(string $algorithm, array $words, array $stems): void 237 | { 238 | 239 | $words = array_map( 240 | fn ($word) => iconv('UTF-8', 'ISO-8859-1', $word), 241 | $words 242 | ); 243 | $stemmer = new Stemmer($algorithm, CharacterEncodingEnum::ISO_8859_1); 244 | $actualStems = array_map( 245 | fn ($stem) => iconv('ISO-8859-1', 'UTF-8', $stem), 246 | $stemmer->stemWords($words) 247 | ); 248 | $this->assertEquals($stems, $actualStems); 249 | 250 | } 251 | 252 | public static function stemWordsIso88591CharencProvider(): array 253 | { 254 | 255 | return [ 256 | ['basque', ['aberatsenetakoa', 'txotxongilo'], ['aberatse', 'txotxongilo']], 257 | ['catalan', ['gratuïtament', 'cuaespinós'], ['gratuit', 'cuaespin']], 258 | ['danish', ['afbildningerne', 'linnedklæderne'], ['afbildning', 'linnedklæd']], 259 | ]; 260 | 261 | } 262 | 263 | /** 264 | * @param string $algorithm 265 | * @param array $words 266 | * @param array $stems 267 | * 268 | * @dataProvider stemWordsIso88592CharencProvider 269 | * @throws UnavailableAlgorithmException 270 | */ 271 | public function testThatStemWordsWithIso88592CharencReturnsTheExpectedStems(string $algorithm, array $words, array $stems): void 272 | { 273 | 274 | $words = array_map( 275 | fn ($word) => iconv('UTF-8', 'ISO-8859-2', $word), 276 | $words 277 | ); 278 | $stemmer = new Stemmer($algorithm, CharacterEncodingEnum::ISO_8859_2); 279 | $actualStems = array_map( 280 | fn ($stem) => iconv('ISO-8859-2', 'UTF-8', $stem), 281 | $stemmer->stemWords($words) 282 | ); 283 | $this->assertEquals($stems, $actualStems); 284 | 285 | } 286 | 287 | public static function stemWordsIso88592CharencProvider(): array 288 | { 289 | 290 | return [ 291 | ['hungarian', ['lenyűgözőnek', 'megháromszorozódott'], ['lenyűgöző', 'megháromszorozódot']], 292 | ]; 293 | 294 | } 295 | 296 | /** 297 | * @param string $algorithm 298 | * @param array $words 299 | * @param array $stems 300 | * 301 | * @dataProvider stemWordsKoi8rCharencProvider 302 | * @throws UnavailableAlgorithmException 303 | */ 304 | public function testThatStemWordsWithKoi8rCharencReturnsTheExpectedStems(string $algorithm, array $words, array $stems): void 305 | { 306 | 307 | $words = array_map( 308 | fn ($word) => iconv('UTF-8', 'KOI8-R', $word), 309 | $words 310 | ); 311 | $stemmer = new Stemmer($algorithm, CharacterEncodingEnum::KOI8_R); 312 | $actualStems = array_map( 313 | fn ($stem) => iconv('KOI8-R', 'UTF-8', $stem), 314 | $stemmer->stemWords($words) 315 | ); 316 | $this->assertEquals($stems, $actualStems); 317 | 318 | } 319 | 320 | public static function stemWordsKoi8rCharencProvider(): array 321 | { 322 | 323 | return [ 324 | ['russian', ['взъерошенный', 'затруднительное'], ['взъерошен', 'затруднительн']], 325 | ]; 326 | 327 | } 328 | 329 | } --------------------------------------------------------------------------------