Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
tests:
name: "Tests (PHP ${{ matrix.php }})"
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php: ['8.1', '8.2', '8.3']
dependencies: ['highest']
include:
- php: '8.1'
dependencies: 'lowest'

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: mbstring, json, redis
coverage: xdebug
tools: composer:v2

- name: Get Composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT

- name: Cache Composer dependencies
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ matrix.php }}-${{ matrix.dependencies }}-${{ hashFiles('**/composer.json') }}
restore-keys: |
${{ runner.os }}-composer-${{ matrix.php }}-${{ matrix.dependencies }}-
${{ runner.os }}-composer-${{ matrix.php }}-
${{ runner.os }}-composer-

- name: Install dependencies (highest)
if: matrix.dependencies == 'highest'
run: composer install --prefer-dist --no-interaction --no-progress

- name: Install dependencies (lowest)
if: matrix.dependencies == 'lowest'
run: composer update --prefer-dist --prefer-lowest --prefer-stable --no-interaction --no-progress

- name: Run tests
run: vendor/bin/phpunit --testdox

coverage:
name: "Code Coverage"
runs-on: ubuntu-latest
needs: tests

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
extensions: mbstring, json, redis
coverage: xdebug
tools: composer:v2

- name: Install dependencies
run: composer install --prefer-dist --no-interaction --no-progress

- name: Run tests with coverage
run: vendor/bin/phpunit --coverage-clover=coverage.xml

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
file: coverage.xml
fail_ci_if_error: false
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

phpstan:
name: "PHPStan"
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
extensions: mbstring, json, redis
tools: composer:v2

- name: Install dependencies
run: composer install --prefer-dist --no-interaction --no-progress

- name: Run PHPStan
run: vendor/bin/phpstan analyse

cs-fixer:
name: "Code Style (informational)"
runs-on: ubuntu-latest
continue-on-error: true

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.1'
extensions: mbstring, json
tools: composer:v2

- name: Install dependencies
run: composer install --prefer-dist --no-interaction --no-progress

- name: Run PHP CS Fixer (dry-run)
run: vendor/bin/php-cs-fixer fix --dry-run --diff
42 changes: 42 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Release

on:
push:
tags:
- 'v*'

jobs:
release:
name: "Create Release"
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Generate changelog
id: changelog
run: |
# Get previous tag
PREVIOUS_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo '')
if [ -z "$PREVIOUS_TAG" ]; then
CHANGELOG=$(git log --oneline --pretty=format:'- %s (%h)' HEAD)
else
CHANGELOG=$(git log --oneline --pretty=format:'- %s (%h)' ${PREVIOUS_TAG}..HEAD)
fi
echo "changelog<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGELOG" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
body: |
## Changes

${{ steps.changelog.outputs.changelog }}
generate_release_notes: true
18 changes: 18 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,19 @@
/vendor/
composer.lock

# IDE
.idea/
.vscode/
*.swp
*.swo

# Build artifacts
coverage.xml
coverage-html/
.phpunit.cache/
.phpstan-cache/
.php-cs-fixer.cache

# OS files
.DS_Store
Thumbs.db
39 changes: 39 additions & 0 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

$finder = PhpCsFixer\Finder::create()
->in([
__DIR__ . '/src',
__DIR__ . '/tests',
])
->name('*.php')
->notName('*.blade.php')
->ignoreDotFiles(true)
->ignoreVCS(true);

return (new PhpCsFixer\Config())
->setRiskyAllowed(false)
->setRules([
'@PSR12' => true,
'array_syntax' => ['syntax' => 'short'],
'ordered_imports' => [
'sort_algorithm' => 'alpha',
'imports_order' => ['class', 'function', 'const'],
],
'no_unused_imports' => true,
'single_quote' => true,
'blank_line_before_statement' => [
'statements' => ['return', 'throw', 'try'],
],
'class_attributes_separation' => [
'elements' => ['method' => 'one'],
],
'phpdoc_align' => ['align' => 'left'],
'phpdoc_order' => true,
'phpdoc_trim' => true,
'no_blank_lines_after_phpdoc' => true,
'return_type_declaration' => ['space_before' => 'none'],
])
->setFinder($finder)
->setCacheFile('.php-cs-fixer.cache');
6 changes: 4 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@
"require": {
"php": "^8.1",
"guzzlehttp/guzzle": "^7.5",
"guzzlehttp/psr7": "^2.4",
"psr/http-client": "^1.0",
"psr/http-factory": "^1.0",
"psr/simple-cache": "^3.0",
"psr/log": "^3.0",
"react/promise": "^2.9"
"react/promise": "^2.9 || ^3.0"
},
"require-dev": {
"phpunit/phpunit": "^10.0",
Expand All @@ -49,8 +50,9 @@
"test": "phpunit",
"phpstan": "phpstan analyse",
"cs-fix": "php-cs-fixer fix",
"cs-check": "php-cs-fixer fix --dry-run --diff",
"check": [
"@cs-fix",
"@cs-check",
"@phpstan",
"@test"
]
Expand Down
24 changes: 24 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
parameters:
level: 6
paths:
- src
tmpDir: .phpstan-cache
reportUnmatchedIgnoredErrors: false
ignoreErrors:
# Missing iterable value type (too noisy at level 6)
-
identifier: missingType.iterableValue
# Redis extension return types include Redis|int|false union
-
message: '#Comparison operation .+ between \(int\|Redis\|false\) and 0#'
paths:
- src/Cache/RedisCacheAdapter.php
-
message: '#Comparison operation .+ between \(bool\|int\|Redis\) and 0#'
paths:
- src/Cache/RedisCacheAdapter.php
# React\Promise\PromiseInterface generic varies between v2 (not generic) and v3 (generic)
-
message: '#generic interface React\\Promise\\PromiseInterface#'
paths:
- src/UltimateLinkChecker.php
29 changes: 29 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true"
failOnRisky="true"
failOnWarning="true"
cacheDirectory=".phpunit.cache"
>
<testsuites>
<testsuite name="Unit">
<directory>tests</directory>
</testsuite>
</testsuites>

<source>
<include>
<directory suffix=".php">src</directory>
</include>
</source>

<coverage>
<report>
<clover outputFile="coverage.xml"/>
<html outputDirectory="coverage-html"/>
</report>
</coverage>
</phpunit>
27 changes: 20 additions & 7 deletions src/Cache/RedisCacheAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Qdenka\UltimateLinkChecker\Cache;

use DateInterval;
use DateTimeImmutable;
use Psr\SimpleCache\CacheInterface;
use Redis;

Expand Down Expand Up @@ -44,14 +45,18 @@ public function set(string $key, mixed $value, DateInterval|int|null $ttl = null
$serialized = serialize($value);

if ($ttl === null) {
return $this->redis->set($prefixedKey, $serialized);
$result = $this->redis->set($prefixedKey, $serialized);

return (bool) $result;
}

if ($ttl instanceof DateInterval) {
$ttl = $this->dateIntervalToSeconds($ttl);
}

return $this->redis->setex($prefixedKey, $ttl, $serialized);
$result = $this->redis->setex($prefixedKey, $ttl, $serialized);

return (bool) $result;
}

/**
Expand All @@ -60,7 +65,9 @@ public function set(string $key, mixed $value, DateInterval|int|null $ttl = null
*/
public function delete(string $key): bool
{
return $this->redis->del($this->getPrefixedKey($key)) > 0;
$result = $this->redis->del($this->getPrefixedKey($key));

return ((int) $result) > 0;
}

/**
Expand All @@ -74,7 +81,9 @@ public function clear(): bool
return true;
}

return $this->redis->del($keys) > 0;
$result = $this->redis->del($keys);

return ((int) $result) > 0;
}

/**
Expand Down Expand Up @@ -125,7 +134,9 @@ public function deleteMultiple(iterable $keys): bool
return true;
}

return $this->redis->del($prefixedKeys) > 0;
$result = $this->redis->del($prefixedKeys);

return ((int) $result) > 0;
}

/**
Expand All @@ -134,7 +145,9 @@ public function deleteMultiple(iterable $keys): bool
*/
public function has(string $key): bool
{
return $this->redis->exists($this->getPrefixedKey($key)) > 0;
$result = $this->redis->exists($this->getPrefixedKey($key));

return ((int) $result) > 0;
}

/**
Expand All @@ -152,7 +165,7 @@ private function getPrefixedKey(string $key): string
*/
private function dateIntervalToSeconds(DateInterval $interval): int
{
$reference = new \DateTimeImmutable();
$reference = new DateTimeImmutable();
$endTime = $reference->add($interval);

return $endTime->getTimestamp() - $reference->getTimestamp();
Expand Down
Loading