Compare commits

..

19 Commits

Author SHA1 Message Date
cliphouse e935e0c6d3 Error test for debug support, try to print out the error
Set correct phpunit version too
2026-06-25 15:16:24 +09:00
cliphouse 5e972b7aec Wrong folders set for PHP check 2026-06-25 15:06:24 +09:00
cliphouse b262b4be0f Update split call for local and remote 2026-06-25 15:04:03 +09:00
cliphouse 5cba8ca9ac Fixes from phpstan checks, add PHP CI/CD github action 2026-06-25 14:49:14 +09:00
Clemens Schwaighofer 397fcb25ff Release: v9.41.1 2026-05-12 11:48:27 +09:00
Clemens Schwaighofer 1e3ce162e5 L10n phpdoc updates 2026-05-12 11:46:19 +09:00
Clemens Schwaighofer 22806d6add README file name update and content fixes for URLs 2026-05-12 10:00:25 +09:00
Clemens Schwaighofer 46e39fa85d publish script update 2026-05-11 17:49:38 +09:00
Clemens Schwaighofer 8eb8e33dc4 Version: 9.41.0.2 2026-05-11 17:46:58 +09:00
Clemens Schwaighofer afcb953d2d Fix publish workflow with auth and no prefix 2026-05-11 17:45:46 +09:00
Clemens Schwaighofer 742b4087aa Update minimum PHP to 8.3 2026-05-11 17:06:09 +09:00
Clemens Schwaighofer f0c8daf26a Version: 9.41.0 actual push 2026-05-11 16:03:39 +09:00
Clemens Schwaighofer ee32a90ff9 Version: 9.41.0 2026-05-11 15:59:33 +09:00
Clemens Schwaighofer 1823fd369b File getMimeType throws exception if no mime type can be set, fixes for phpstan checks 2026-05-11 15:55:33 +09:00
Clemens Schwaighofer 116f0a770c Switch to dotenv v3 instead of dev-master 2026-04-20 16:21:43 +09:00
Clemens Schwaighofer b2123a183d Version: v9.40.0.1 2026-04-17 12:21:53 +09:00
Clemens Schwaighofer e92c585638 Set all to stable for composer packages 2026-04-17 12:20:10 +09:00
Clemens Schwaighofer 82c982c2c7 composer repository update 2026-02-19 14:35:39 +09:00
Clemens Schwaighofer 6b3ef52c5d Release: v9.40.0 2026-01-22 11:47:55 +09:00
29 changed files with 590 additions and 188 deletions
-33
View File
@@ -1,33 +0,0 @@
name: CI
run-name: ${{ github.actor}} runs CI
on: [push]
jobs:
phpstan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# - uses: php-actions/composer@v6
# env:
# COMPOSER_ROOT_VERSION: dev-master
- name: "Restore result cache"
uses: actions/cache/restore@v4
with:
path: ./tmp
key: "result-cache-v1-${{ matrix.php-version }}-${{ github.run_id }}"
restore-keys: |
result-cache-v1-${{ matrix.php-version }}-
- name: PHPStan Static Analysis
uses: php-actions/phpstan@v3
with:
path: src/
- name: "Save result cache"
uses: actions/cache/save@v4
if: always()
with:
path: ./tmp
key: "result-cache-v1-${{ matrix.php-version }}-${{ github.run_id }}"
# - name: PHPunit Tests
# run: |
# vendor/bin/phpunit
@@ -0,0 +1,37 @@
name: Call PHP Static Checks and Tests
run-name: ${{ github.actor }} calls Content.github-actions/php-static-checks-and-tests
on:
workflow_dispatch:
push:
branches: [master, staging, development]
jobs:
call-check-and-build-local:
# call local workflow if repository name is NOT CoreLibs-Composer-All
if: github.event.repository.name != 'CoreLibs-Composer-All'
uses: ./.github/workflows/php-static-checks-and-tests.yml
with:
php-version: '8.4,8.5'
phpunit-version: '^9'
composer-directory: './'
source-directory: './src'
disable-phan: ${{ vars.DISABLE_PHAN_CHECK == 'true' || false }}
secrets:
opj-package-user: ${{ secrets.OPJ_PACKAGE_USER }}
opj-package-api-key: ${{ secrets.OPJ_PACKAGE_API_KEY }}
call-check-and-build-remote:
# call remote workflow if repository name is CoreLibs-Composer-All
if: github.event.repository.name == 'CoreLibs-Composer-All'
uses: OmnicomProduction-Japan/Content.github-actions/.github/workflows/php-static-checks-and-tests.yml@v1
with:
php-version: '8.4,8.5'
phpunit-version: '^9'
composer-directory: './'
source-directory: './src'
disable-phan: ${{ vars.DISABLE_PHAN_CHECK == 'true' || false }}
secrets:
opj-package-user: ${{ secrets.OPJ_PACKAGE_USER }}
opj-package-api-key: ${{ secrets.OPJ_PACKAGE_API_KEY }}
@@ -0,0 +1,309 @@
# external call not yet setup, only copy to repo at the moment
name: PHP Static Checks and Tests Matrix Workflow
run-name: ${{ github.actor}} runs PHP-MATRIX-CI
on:
workflow_call:
inputs:
php-version:
type: string
required: false
default: '8.5'
description: 'PHP versions to use for the checks and tests, default is 8.4 and 8.5, can be a comma-separated list of versions'
phpunit-version:
type: string
required: false
default: '^13'
description: 'PHPUnit versions to use for the tests, default is ^13, can be a comma-separated list of versions'
operating-system:
type: string
required: false
default: 'ubuntu-latest'
description: 'Operating systems to run the tests on, default is ubuntu-latest, can be a comma-separated list of operating systems'
disable-phan:
type: boolean
default: false
required: false
description: 'Set to "true" to skip running phan'
composer-directory:
type: string
required: false
default: './'
description: 'Directory where composer.json is located, relative to the repository root'
source-directory:
type: string
required: false
default: './src'
description: 'Directory where the PHP source code is located, relative to the repository root'
secrets:
opj-package-user:
required: true
description: 'Username for private package repository'
opj-package-api-key:
required: true
description: 'API key for private package repository'
jobs:
setup:
runs-on: ubuntu-latest
outputs:
php-version: ${{ steps.set-matrix.outputs.php-version }}
phpunit-version: ${{ steps.set-matrix.outputs.phpunit-version }}
operating-system: ${{ steps.set-matrix.outputs.operating-system }}
steps:
- name: Set matrix values
id: set-matrix
run: |
echo "php-version=$(echo '${{ inputs.php-version }}' | tr ',' '\n' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | jq -R . | jq -cs .)" >> $GITHUB_OUTPUT
echo "phpunit-version=$(echo '${{ inputs.phpunit-version }}' | tr ',' '\n' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | jq -R . | jq -cs .)" >> $GITHUB_OUTPUT
echo "operating-system=$(echo '${{ inputs.operating-system }}' | tr ',' '\n' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | jq -R . | jq -cs .)" >> $GITHUB_OUTPUT
php-check-and-test-matrix:
needs: setup
runs-on: ${{ matrix.operating-system }}
strategy:
matrix:
operating-system: ${{fromJSON(needs.setup.outputs.operating-system)}}
php-version: ${{fromJSON(needs.setup.outputs.php-version)}}
phpunit-version: ${{fromJSON(needs.setup.outputs.phpunit-version)}}
# include:
# - operating-system: ubuntu-latest
# php-version: '8.4'
# phpunit-version: '^13'
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
extensions: mbstring, intl, sqlite, pdo_sqlite
# ini-values: post_max_size=256M, max_execution_time=180
tools: composer, ${{ inputs.disable-phan == false && 'phan, ' || '' }}phpstan, phpcs, phpunit:${{ matrix.phpunit-version }}, cs2pr
# coverage: pcov
coverage: xdebug
# env:
# # Does not work, hast to be set in dependencies step, see below in "install dependencies"
# COMPOSER_AUTH: |
# {
# "http-basic": {
# "packages.omnicomproduction.jp": {
# "username": "${{ secrets.OPJ_PACKAGE_USER }}",
# "password": "${{ secrets.OPJ_PACKAGE_API_KEY }}"
# }
# }
# }
- name: Check Tools setup
id: check-tools
run: |
REQUIRED_TOOLS=("composer" "phpunit" "phpstan" ${{ inputs.disable-phan == false && '"phan" ' || '' }}"phpcs" "cs2pr")
SKIP_IF_FAILED=("phan")
for tool in "${REQUIRED_TOOLS[@]}"; do
if ! command -v "$tool" &> /dev/null; then
if [[ " ${SKIP_IF_FAILED[@]} " =~ " ${tool} " ]]; then
echo "⚠️ Warning: $tool is not installed, but it's optional. Skipping..."
echo "${tool}=skip" >> $GITHUB_OUTPUT
continue
fi
echo "❌ Error: $tool failed to load!"
exit 1
else
# the tool path does not have "setup-php/tools/ inside
tool_path=$(which "$tool")
if [[ "$tool_path" != */setup-php/tools/* ]]; then
if [[ " ${SKIP_IF_FAILED[@]} " =~ " ${tool} " ]]; then
echo "⚠️ Warning: $tool installation check failed, but it's optional. Skipping..."
echo "${tool}=skip" >> $GITHUB_OUTPUT
continue
fi
echo "❌ Error: $tool path lookup failed!"
exit 1
fi
tool_version=$("$tool" --version 2>&1)
echo "✅ $tool loaded successfully: $tool_version ($(which $tool))"
echo "${tool}=ok" >> $GITHUB_OUTPUT
fi
done
- name: Check source folder and composer.json setup
run: |
if [ ! -d "${{ inputs.source-directory }}" ]; then
echo "❌ Error: Source directory '${{ inputs.source-directory }}' does not exist!"
exit 1
fi
if [ ! -f "${{ inputs.composer-directory }}/composer.json" ]; then
echo "❌ Error: composer.json not found in '${{ inputs.composer-directory }}'!"
exit 1
fi
- name: Setup problem matchers for PHP
run: echo "::add-matcher::${{ runner.tool_cache }}/php.json"
- name: Setup problem matchers for PHPUnit
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
- name: Get composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
working-directory: ${{ inputs.composer-directory }}
- name: Cache dependencies
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install dependencies
run: |
composer config http-basic.packages.omnicomproduction.jp "${{ secrets.opj-package-user }}" "${{ secrets.opj-package-api-key }}"
composer install --prefer-dist
working-directory: ${{ inputs.composer-directory }}
- name: Run composer audit
id: run-composer-audit
run: composer audit --format=json --no-scripts --no-plugins --no-interaction > composer-audit-output.json
# some post action with the composer output format file
working-directory: ${{ inputs.composer-directory }}
- name: Run phpcs
id: run-phpcs
run: phpcs -q --report=checkstyle --standard=phpcs.xml --ignore=vendor/ ${{ inputs.source-directory }} | cs2pr
- name: Run phan
id: run-phan
if: ${{ ! inputs.disable-phan && steps.check-tools.outputs.phan == 'ok' }}
run: phan --analyze-twice --no-progress-bar --config-file=./.phan/config.php
# Must have "./tmp" directory set in configuration
# if we have "./tmp/phpstan" or anything different adjust the path below
- name: "Restore result cache"
uses: actions/cache/restore@v5
with:
path: ./tmp
key: "result-cache-${{ github.run_id }}"
restore-keys: |
result-cache-v1-
- name: Run PHPStan
id: run-phpstan
run: phpstan analyse --no-progress --configuration=phpstan.neon
- name: "Save result cache"
uses: actions/cache/save@v5
if: always()
with:
path: ./tmp
key: "result-cache-${{ github.run_id }}"
- name: Test with phpunit
id: run-phpunit
run: |
phpunit --colors=never \
--no-coverage \
--coverage-text=phpunit-coverage.txt \
--only-summary-for-coverage-text \
--coverage-openclover=phpunit-openclover.xml
- name: Code Coverage Summary Report
uses: saschanowak/CloverCodeCoverageSummary@1.1.1
with:
filename: phpunit-openclover.xml
- name: Summary
if: always()
env:
COMPOSER_AUDIT_RESULT: ${{ steps.run-composer-audit.outcome }}
COMPOSER_AUDIT_FILE: ${{ inputs.composer-directory }}/composer-audit-output.json
PHPCS_RESULT: ${{ steps.run-phpcs.outcome }}
PHAN_RESULT: ${{ steps.run-phan.outcome }}
PHPSTAN_RESULT: ${{ steps.run-phpstan.outcome }}
PHPUNIT_RESULT: ${{ steps.run-phpunit.outcome }}
run: |
icon() { if [[ "$1" == "success" ]]; then echo "✅"; elif [[ "$1" == "skipped" ]]; then echo "⏭️"; else echo "❌"; fi; }
row() { echo "<tr><td><b>$1</b></td><td>$(icon $2) $2</td></tr>"; }
cat >> $GITHUB_STEP_SUMMARY << EOF
<h2>PHP CI Results</h2>
<table>
<thead>
<tr><th>Check</th><th>Status</th></tr>
</thead>
<tbody>
$(row "composer audit" "$COMPOSER_AUDIT_RESULT")
$(row "phpcs" "$PHPCS_RESULT")
$(row "phan" "$PHAN_RESULT")
$(row "phpstan" "$PHPSTAN_RESULT")
$(row "phpunit" "$PHPUNIT_RESULT")
</tbody>
</table>
<details>
<summary>Run details</summary>
| Input | Value |
| --- | --- |
| PHP version | \`${{ inputs.php-version }}\` |
| PHPUnit version | \`${{ inputs.phpunit-version }}\` |
| Operating system | \`${{ inputs.operating-system }}\` |
| Phan disabled | \`${{ inputs.disable-phan }}\` |
| Composer directory | \`${{ inputs.composer-directory }}\` |
| Source directory | \`${{ inputs.source-directory }}\` |
</details>
EOF
if [ -f phpunit-coverage.txt ]; then
{
echo "<h3>Code Coverage</h3>"
echo ""
echo "| Metric | Coverage | Status |"
echo "| --- | --- | --- |"
grep -E '^\s+(Classes|Methods|Lines):' phpunit-coverage.txt | head -3 | while IFS= read -r line; do
name=$(echo "$line" | sed 's/^[[:space:]]*//' | cut -d: -f1)
value=$(echo "$line" | cut -d: -f2- | sed 's/^[[:space:]]*//')
pct=$(echo "$value" | grep -oE '[0-9]+\.[0-9]+' | head -1)
circle=$(awk -v p="$pct" 'BEGIN { if (p+0 == 100) print "🟢"; else if (p+0 >= 60) print "🟠"; else print "🔴" }')
echo "| $name | $value | $circle |"
done
echo ""
echo "<details><summary>Full coverage report</summary>"
echo ""
echo '```'
cat phpunit-coverage.txt
echo '```'
echo ""
echo "</details>"
} >> $GITHUB_STEP_SUMMARY
fi
cat code-coverage-summary.md >> $GITHUB_STEP_SUMMARY
cat code-coverage-details.md >> $GITHUB_STEP_SUMMARY
if [ -f "$COMPOSER_AUDIT_FILE" ] && jq empty "$COMPOSER_AUDIT_FILE" 2>/dev/null; then
{
echo "<h3>Composer Audit</h3>"
echo ""
PACKAGES_AUDITED=$(jq -r '.metadata["packages-audited"] // "?"' "$COMPOSER_AUDIT_FILE")
ADVISORIES_COUNT=$(jq -r '.metadata["packages-with-advisories"] // 0' "$COMPOSER_AUDIT_FILE")
echo "| Packages audited | With advisories |"
echo "| --- | --- |"
echo "| $PACKAGES_AUDITED | $ADVISORIES_COUNT |"
echo ""
ADVISORY_ROWS=$(jq -r '
.packages[]
| select(.advisories and (.advisories | length) > 0)
| .name as $name | .version as $ver
| .advisories[]
| "| \($name) | \($ver) | \(.cve // "N/A") | \(.title) |"
' "$COMPOSER_AUDIT_FILE" 2>/dev/null || true)
if [ -n "$ADVISORY_ROWS" ]; then
echo "| Package | Version | CVE | Title |"
echo "| --- | --- | --- | --- |"
echo "$ADVISORY_ROWS"
echo ""
fi
echo "<details><summary>Full audit output</summary>"
echo ""
echo '```json'
cat "$COMPOSER_AUDIT_FILE"
echo '```'
echo ""
echo "</details>"
} >> $GITHUB_STEP_SUMMARY
fi
+2 -2
View File
@@ -54,8 +54,8 @@ return [
// Note that the **only** effect of choosing `'5.6'` is to infer that functions removed in php 7.0 exist.
// (See `backward_compatibility_checks` for additional options)
// Automatically inferred from composer.json requirement for "php" of ">=8.2"
'target_php_version' => '8.2',
"minimum_target_php_version" => "8.2",
'target_php_version' => '8.3',
"minimum_target_php_version" => "8.3",
// turn color on (-C)
"color_issue_messages_if_supported" => true,
+2 -2
View File
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<phive xmlns="https://phar.io/phive">
<phar name="phpunit" version="^9.6" installed="9.6.31" location="./tools/phpunit" copy="false"/>
<phar name="phpunit" version="^9.6" installed="9.6.34" location="./tools/phpunit" copy="false"/>
<phar name="phpcs" version="^4.0.0" installed="4.0.1" location="./tools/phpcs" copy="false"/>
<phar name="phpcbf" version="^4.0.0" installed="4.0.1" location="./tools/phpcbf" copy="false"/>
<phar name="psalm" version="^5.15.0" installed="5.26.1" location="./tools/psalm" copy="false"/>
<phar name="phpstan" version="^2.0.0" installed="2.1.33" location="./tools/phpstan" copy="false"/>
<phar name="phpstan" version="^2.0.0" installed="2.2.2" location="./tools/phpstan" copy="false"/>
<phar name="phan" version="^5.4.2" installed="5.5.2" location="./tools/phan" copy="false"/>
</phive>
@@ -17,7 +17,7 @@ Run `publish/publish.sh` script to create composer packages.
This will automatically run all commands to create the packages
### composer.egplusww.jp web host
### composer.omnicomproduction.jp web host
For the local composer package host.
@@ -30,7 +30,7 @@ The entry is a copy of the `composer.json` with the following new entries:
"version": "X.Y.Z",
...
"dist": {
"url": "https://git.egplusww.jp/Composer/CoreLibs-Composer-All/archive/vX.Y.Z.zip",
"url": "https://packages.omnicomproduction.jp/Composer/CoreLibs-Composer-All/archive/vX.Y.Z.zip",
"type": "zip"
},
...
View File
+10 -10
View File
@@ -15,10 +15,11 @@
"email": "clemens.schwaighofer@egplusww.com"
}
],
"minimum-stability": "dev",
"minimum-stability": "stable",
"prefer-stable": true,
"require": {
"php": ">=8.2",
"psr/log": "^3.0@dev"
"php": ">=8.3",
"psr/log": "^3.0"
},
"require-dev": {
"phpstan/phpstan": "^2.0",
@@ -26,13 +27,12 @@
"phpstan/phpstan-deprecation-rules": "^2.0",
"phan/phan": "^5.4",
"egrajp/smarty-extended": "^5.4",
"gullevek/dotenv": "dev-master",
"gullevek/dotenv": "^3",
"phpunit/phpunit": "^9"
},
"repositories": {
"git.egplusww.jp.Composer": {
"type": "composer",
"url": "https://git.egplusww.jp/api/packages/Composer/composer"
}
}
"repositories": [{
"name": "packages.omnicomproduction.Composer",
"type": "composer",
"url": "https://packages.omnicomproduction.jp/api/packages/Composer/composer"
}]
}
+15 -1
View File
@@ -1,7 +1,8 @@
<phpunit
cacheResultFile="/tmp/phpunit-corelibs-composer.result.cache"
colors="true"
verbose="true"
verbose="false"
convertDeprecationsToExceptions="true"
bootstrap="test/phpunit/bootstrap.php"
>
<testsuites>
@@ -9,4 +10,17 @@
<directory>test/phpunit</directory>
</testsuite>
</testsuites>
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">./www/lib/CoreLibs</directory>
</include>
<!-- <report>
<html outputDirectory="build/coverage-report"/>
</report> -->
</coverage>
<!-- <source>
<include>
<directory>./src</directory>
</include>
</source> -->
</phpunit>
+1 -1
View File
@@ -1 +1 @@
9.39.1
9.41.1
+32 -14
View File
@@ -11,6 +11,9 @@ go_flag="$1";
function gitea_publish
{
# MUST have in app.ini, else upload will fail
# [repository]
# PREFIX_ARCHIVE_FILES = false
_GITEA_PUBLISH="${1}"
_GITEA_UPLOAD_FILENAME="${2}"
_GITEA_URL_DL="${3}"
@@ -28,26 +31,40 @@ function gitea_publish
[ -n "${_GITEA_URL_DL}" ] && [ -n "${_GITEA_URL_PUSH}" ] &&
[ -n "${_GITEA_USER}" ] && [ -n "${_GITEA_TOKEN}" ]; then
echo "> Publish ${_GITEA_UPLOAD_FILENAME} with ${_VERSION} to: ${_GITEA_URL_PUSH}";
error=0;
if [ ! -f "${_PACKAGE_DOWNLOAD}${_GITEA_UPLOAD_FILENAME}-v${_VERSION}.zip" ]; then
echo "> Download: ${_GITEA_UPLOAD_FILENAME}-v${_VERSION}.zip";
curl -LJO \
--output-dir "${_PACKAGE_DOWNLOAD}" \
"${_GITEA_URL_DL}"/v"${_VERSION}".zip;
http_response=$(curl -LJO \
-w "%{http_code}" \
--output-dir "${_PACKAGE_DOWNLOAD}" \
-H "Authorization: token ${_GITEA_TOKEN}" \
"${_GITEA_URL_DL}/v${_VERSION}.zip"
);
if [ "$http_response" -ne 200 ]; then
echo "[!] Error downloading file: ${http_response}";
error=1;
fi;
fi;
if [ ! -f "${_PACKAGE_DOWNLOAD}${_GITEA_UPLOAD_FILENAME}-v${_VERSION}.zip" ]; then
echo "[!] Package file does not exist for version: ${_VERSION}";
else
response=$(curl --user "${_GITEA_USER}":"${_GITEA_TOKEN}" \
if [ $error -eq 0 ]; then
# if this is the short name rename to long name, if long name down load does not work
if [ -f "${_PACKAGE_DOWNLOAD}v${_VERSION}.zip" ]; then
mv "${_PACKAGE_DOWNLOAD}v${_VERSION}.zip" "${_PACKAGE_DOWNLOAD}${_GITEA_UPLOAD_FILENAME}-v${_VERSION}.zip";
fi;
if [ ! -f "${_PACKAGE_DOWNLOAD}${_GITEA_UPLOAD_FILENAME}-v${_VERSION}.zip" ]; then
echo "[!] Package file does not exist for version: ${_VERSION}";
else
response=$(curl --user "${_GITEA_USER}":"${_GITEA_TOKEN}" \
--upload-file "${_PACKAGE_DOWNLOAD}${_GITEA_UPLOAD_FILENAME}-v${_VERSION}.zip" \
"${_GITEA_URL_PUSH}"?version="${_VERSION}");
status=$(echo "${response}" | jq .errors[].status);
message=$(echo "${response}" | jq .errors[].message);
if [ -n "${status}" ]; then
echo "[!] Error ${status}: ${message}";
else
echo "> Publish completed";
status=$(echo "${response}" | jq .errors[].status);
message=$(echo "${response}" | jq .errors[].message);
if [ -n "${status}" ]; then
echo "[!] Error ${status}: ${message}";
else
echo "> Publish completed";
echo "${_VERSION}" > "${_file_last_published}";
fi;
fi;
echo "${_VERSION}" > "${_file_last_published}";
fi;
else
echo "[!] Missing either GITEA_UPLOAD_FILENAME, GITEA_URL_DL, GITEA_URL_PUSH, GITEA_USER or GITEA_TOKEN environment variable";
@@ -88,6 +105,7 @@ if [ -f "${file_last_published}" ]; then
LAST_PUBLISHED_VERSION=$(cat "${file_last_published}");
if dpkg --compare-versions "${VERSION}" le "${LAST_PUBLISHED_VERSION}"; then
echo "[!] git tag version ${VERSION} is not newer than previous published version ${LAST_PUBLISHED_VERSION}";
exit;
fi;
fi;
+9 -3
View File
@@ -58,15 +58,21 @@ class File
* else returns '' for any other finfo read problem
*
* @param string $read_file File to read, relative or absolute path
* @return string
* @return string mime type
* @throws \UnexpectedValueException if file cannot be read or is not a file
* @throws \RangeException if we cannot get a mime type and throw exception is on
*/
public static function getMimeType(string $read_file): string
{
$finfo = new \finfo(FILEINFO_MIME_TYPE);
if (!is_file($read_file)) {
if (!is_file($read_file) || !is_readable($read_file)) {
throw new \UnexpectedValueException('[getMimeType] File not found: ' . $read_file);
}
return $finfo->file($read_file) ?: '';
$mime_type = $finfo->file($read_file);
if ($mime_type === false) {
throw new \RangeException('[getMimeType] Cannot get mime type for: ' . $read_file);
}
return $mime_type;
}
}
+2 -2
View File
@@ -133,7 +133,7 @@ class DateTime
} else {
for ($i = 0, $iMax = count($timegroups); $i < $iMax; $i++) {
$output = floor((float)$timestamp / $timegroups[$i]);
$timestamp = (float)$timestamp % $timegroups[$i];
$timestamp = $timestamp % $timegroups[$i];
// output has days|hours|min|sec
if ($output || $time_string) {
$time_string .= $output . $labels[$i] . (($i + 1) != count($timegroups) ? ' ' : '');
@@ -453,7 +453,7 @@ class DateTime
}
if (isset($matches[11]) && is_numeric($matches[11])) {
// for milliseconds, we need to divide by 1000 and add them
$timestamp += (float)($matches[11] / 1000);
$timestamp += ((float)$matches[11] / 1000);
}
if ($negative) {
// cast to float so we can do a negative multiplication
+62 -63
View File
@@ -43,71 +43,70 @@ class Byte
public static function humanReadableByteFormat(string|int|float $bytes, int $flags = 0): string
{
// if not numeric, return as is
if (is_numeric($bytes)) {
// flags bit wise check
// remove space between number and suffix
if ($flags & self::BYTE_FORMAT_NOSPACE) {
$space = false;
} else {
$space = true;
}
// use sprintf instead of round
if ($flags & self::BYTE_FORMAT_ADJUST) {
$adjust = true;
} else {
$adjust = false;
}
// use SI 1000 mod and not 1024 mod
if ($flags & self::BYTE_FORMAT_SI) {
$si = true;
} else {
$si = false;
}
if ($flags > 7) {
throw new \InvalidArgumentException("Invalid flags parameter: $flags", 1);
}
// si or normal
$unit = $si ? 1000 : 1024;
// always positive
$abs_bytes = $bytes == PHP_INT_MIN ? PHP_INT_MAX : abs((float)$bytes);
// smaller than unit is always B
if ($abs_bytes < $unit) {
return $bytes . 'B';
}
// labels in order of size [Y, Z]
$labels = ['', 'K', 'M', 'G', 'T', 'P', 'E'];
// exp position calculation
$exp = (int)floor(log($abs_bytes, $unit));
// avoid printing out anything larger than max labels
if ($exp >= count($labels)) {
$exp = count($labels) - 1;
}
// deviation calculation
$dev = pow($unit, $exp) * ($unit - 0.05);
// shift the exp +1 for on the border units
if (
$exp < 6 &&
$abs_bytes > ($dev - (((int)$dev & 0xfff) == 0xd00 ? 52 : 0))
) {
$exp++;
}
// label name, including leading space if flagged
$pre = ($space ? ' ' : '') . ($labels[$exp] ?? '>E') . ($si ? 'i' : '') . 'B';
$bytes_calc = $abs_bytes / pow($unit, $exp);
// if original is negative, reverse
if ($bytes < 0) {
$bytes_calc *= -1;
}
if ($adjust) {
return sprintf("%.2f%s", $bytes_calc, $pre);
} else {
return round($bytes_calc, 2) . $pre;
}
} else {
if (!is_numeric($bytes)) {
// if anything other return as string
return (string)$bytes;
}
// flags bit wise check
// remove space between number and suffix
if ($flags & self::BYTE_FORMAT_NOSPACE) {
$space = false;
} else {
$space = true;
}
// use sprintf instead of round
if ($flags & self::BYTE_FORMAT_ADJUST) {
$adjust = true;
} else {
$adjust = false;
}
// use SI 1000 mod and not 1024 mod
if ($flags & self::BYTE_FORMAT_SI) {
$si = true;
} else {
$si = false;
}
if ($flags > 7) {
throw new \InvalidArgumentException("Invalid flags parameter: $flags", 1);
}
// si or normal
$unit = $si ? 1000 : 1024;
// always positive
$abs_bytes = $bytes == PHP_INT_MIN ? PHP_INT_MAX : abs((float)$bytes);
// smaller than unit is always B
if ($abs_bytes < $unit) {
return $bytes . 'B';
}
// labels in order of size [Y, Z]
$labels = ['', 'K', 'M', 'G', 'T', 'P', 'E'];
// exp position calculation
$exp = (int)floor(log($abs_bytes, $unit));
// avoid printing out anything larger than max labels
if ($exp >= count($labels)) {
$exp = count($labels) - 1;
}
// deviation calculation
$dev = pow($unit, $exp) * ($unit - 0.05);
// shift the exp +1 for on the border units
if (
$exp < 6 &&
$abs_bytes > ($dev - (((int)$dev & 0xfff) == 0xd00 ? 52 : 0))
) {
$exp++;
}
// label name, including leading space if flagged
$pre = ($space ? ' ' : '') . ($labels[$exp] ?? '>E') . ($si ? 'i' : '') . 'B';
$bytes_calc = $abs_bytes / pow($unit, $exp);
// if original is negative, reverse
if ($bytes < 0) {
$bytes_calc *= -1;
}
if ($adjust) {
return sprintf("%.2f%s", $bytes_calc, $pre);
} else {
return round($bytes_calc, 2) . $pre;
}
}
/**
@@ -154,7 +153,7 @@ class Byte
if (!empty($matches[1])) {
$number_negative = true;
}
if (isset($matches[2]) && isset($matches[3])) {
if (isset($matches[2])) {
// remove all non valid characters from the number
$number = preg_replace('/[^0-9\.]/', '', $matches[2]);
// final clean up and convert to float
+6 -6
View File
@@ -957,9 +957,9 @@ class Color
* Undocumented function
*
* @param HWB $hwb
* @return Lch
* @return LCH
*/
public static function hwbToLch(HWB $hwb): Lch
public static function hwbToLch(HWB $hwb): LCH
{
return self::rgbToLch(
self::hwbToRgb($hwb)
@@ -1078,10 +1078,10 @@ class Color
/**
* OkLab to Lch (CIE)
*
* @param LAB $lab
* @param Lab $lab
* @return LCH
*/
public static function okLabToLch(LAB $lab): LCH
public static function okLabToLch(Lab $lab): LCH
{
return self::labToLch(
self::okLabToLab($lab)
@@ -1092,9 +1092,9 @@ class Color
* Lch (CIE) to OkLab
*
* @param LCH $lch
* @return LAB
* @return Lab
*/
public static function lchToOkLab(LCH $lch): LAB
public static function lchToOkLab(LCH $lch): Lab
{
return self::labToOkLab(
self::lchToLab($lch)
+5 -1
View File
@@ -74,11 +74,15 @@ class Json
* Weill set the internval last error state and info can be read with jsonGetLastError
*
* @param string $json
* @param int $flags only JSON_INVALID_UTF8_IGNORE is currently allowed
* @param int $flags only JSON_INVALID_UTF8_IGNORE is currently allowed
* @return bool
* @throws \InvalidArgumentException if flags is invalid
*/
public static function jsonValidate(string $json, int $flags = 0): bool
{
if ($flags != 0 && $flags != JSON_INVALID_UTF8_IGNORE) {
throw new \InvalidArgumentException("For flag only 0 or JSON_INVALID_UTF8_IGNORE is allwed: " . $flags);
}
$json_valid = json_validate($json, flags:$flags);
self::$json_last_error = json_last_error();
return $json_valid;
+15 -9
View File
@@ -2232,9 +2232,9 @@ class IO
$milliseconds = $matches[13] ?? '';
// clean up, hide entries that have 00 in the time group
$hour = $hour != '00' ? preg_replace('/^0/', '', $hour) : '';
$minutes = $minutes != '00' ? preg_replace('/^0/', '', $minutes) : '';
$seconds = $seconds != '00' ? preg_replace('/^0/', '', $seconds) : '';
$hour = !empty($hour) ? preg_replace('/^0/', '', $hour) : '';
$minutes = !empty($minutes) ? preg_replace('/^0/', '', $minutes) : '';
$seconds = !empty($seconds) ? preg_replace('/^0/', '', $seconds) : '';
// strip any leading or trailing spaces
$time_string = trim(
@@ -2562,21 +2562,27 @@ class IO
// set field names
$this->cursor_ext[$query_hash]['field_names'] = [];
for ($i = 0; $i < $this->cursor_ext[$query_hash]['num_fields']; $i++) {
$this->cursor_ext[$query_hash]['field_names'][] =
$this->db_functions->__dbFieldName(
if (
($field_name = $this->db_functions->__dbFieldName(
$this->cursor_ext[$query_hash]['cursor'],
$i
);
)) !== false
) {
$this->cursor_ext[$query_hash]['field_names'][] = $field_name;
}
}
$this->field_names = $this->cursor_ext[$query_hash]['field_names'];
// field types
$this->cursor_ext[$query_hash]['field_types'] = [];
for ($i = 0; $i < $this->cursor_ext[$query_hash]['num_fields']; $i++) {
$this->cursor_ext[$query_hash]['field_types'][] =
$this->db_functions->__dbFieldType(
if (
($field_type = $this->db_functions->__dbFieldType(
$this->cursor_ext[$query_hash]['cursor'],
$i
);
)) !== false
) {
$this->cursor_ext[$query_hash]['field_types'][] = $field_type;
}
}
$this->field_types = $this->cursor_ext[$query_hash]['field_types'];
// combined name => type
+1 -1
View File
@@ -51,7 +51,7 @@ enum Convert: int
/**
* Get internal name from int value
*
* @param int $value
* @param int $value
* @return self
*/
public static function fromValue(int $value): self
+2 -2
View File
@@ -134,8 +134,8 @@ class Support
* Convert bool value to string value. Short name alias for printBool
*
* @param bool $bool Bool value to be transformed
* @param string $true [='true'] Override default string 'true'
* @param string $false [=false'] Override default string 'false'
* @param string $true [=true] Override default string 'true'
* @param string $false [=false] Override default string 'false'
* @return string $true or $false string for true/false bool
*/
public static function prBl(
+3 -3
View File
@@ -146,7 +146,7 @@ class L10n
* @param string $locale language name, if not set, try previous set
* @param string $domain set name for mo file, if not set, try previous set
* @param string $path path, if not set try to get from paths array, else self
* @param string $override_encoding if locale does not env encoding set, use this one
* @param string $override_encoding if locale does not have encoding set, use this one
* @return GetTextReader the main gettext reader object
*/
public function getTranslator(
@@ -301,7 +301,7 @@ class L10n
* Get the local as array same to the GetLocale::setLocale return
* This does not set from outside, but only what is set in the l10n class
*
* @return array{locale: string, lang: string, lang_short: string, domain: string, encoding: string, path: string}
* @return array{locale:string,lang:string,lang_short:string,domain:string,encoding:string,path:string}
*/
public function getLocaleAsArray(): array
{
@@ -321,7 +321,7 @@ class L10n
* parse the locale string for further processing
*
* @param string $locale Locale to parse
* @return array<string,string|null> array with lang, country, charset, modifier
* @return array{lang:string|null,country:string|null,charset:string|null,modifier:string|null} parsed locale
*/
public static function parseLocale(string $locale = ''): array
{
+1 -1
View File
@@ -63,7 +63,7 @@ enum Flag: int
/**
* Get internal name from int value
*
* @param int $value
* @param value-of<self::VALUES> $value
* @return self
*/
public static function fromValue(int $value): self
+2 -2
View File
@@ -86,7 +86,7 @@ enum Level: int
/**
* @param value-of<self::NAMES>|LogLevel::*|'Debug'|'Info'|'Notice'|'Warning'|'Error'|'Critical'|'Alert'|'Emergency' $name
* @return static
* @return self
*/
public static function fromName(string $name): self
{
@@ -104,7 +104,7 @@ enum Level: int
/**
* @param value-of<self::VALUES> $value
* @return static
* @return self
*/
public static function fromValue(int $value): self
{
+25 -18
View File
@@ -178,11 +178,11 @@ class Elements
"/(href=\")?(\>)?\b($protRegex)([\w\.\-?&=+%#~,;\/]+)\b([\.\-?&=+%#~,;\/]*)(\|([^\||^#]+)(#([^\|]+))?\|)?/",
function ($matches) {
return self::createUrl(
$matches[1] ?? '',
$matches[2] ?? '',
$matches[3] ?? '',
$matches[4] ?? '',
$matches[5] ?? '',
$matches[1],
$matches[2],
$matches[3],
$matches[4],
$matches[5],
$matches[7] ?? '',
$matches[9] ?? ''
);
@@ -232,8 +232,15 @@ class Elements
* @param string $class style sheet
* @return string correct string for url href process
*/
private static function createUrl($href, $atag, $_1, $_2, $_3, $name, $class): string
{
private static function createUrl(
string $href,
string $atag,
string $_1,
string $_2,
string $_3,
string $name,
string $class
): string {
// $this->debug('URL', "1: $_1 - 2: $_2 - $_3 - atag: $atag - name: $name - class: $class");
// if $_1 ends with //, then we strip $_1 complete & target is also blanked (its an internal link)
if (preg_match("/\/\/$/", $_1) && preg_match("/^\//", $_2)) {
@@ -250,11 +257,8 @@ class Elements
. "##GT##" . ($name ? $name : $_2 . $_3) . "##LT##/a##GT##";
} elseif ($href && !$atag) {
return "href=##QUOT##$_1$_2$_3##QUOT##";
} elseif ($atag) {
return $atag . $_2 . $_3;
} else {
return $href;
}
return $atag . $_2 . $_3;
}
/**
@@ -269,8 +273,15 @@ class Elements
* @param string $class style sheet
* @return string created html email a href string
*/
private static function createEmail($mailto, $atag, $_1, $_2, $_3, $title, $class)
{
private static function createEmail(
string $mailto,
string $atag,
string $_1,
string $_2,
string $_3,
string $title,
string $class
): string {
$email = $_1 . "@" . $_2 . "." . $_3;
if (!$mailto && !$atag) {
return "##LT##a href=##QUOT##mailto:" . $email . "##QUOT##"
@@ -278,12 +289,8 @@ class Elements
. "##GT##" . ($title ? $title : $email) . "##LT##/a##GT##";
} elseif ($mailto && !$atag) {
return "mailto:" . $email;
} elseif ($atag) {
return $atag . $email;
} else {
// else just return email as is
return $email;
}
return $atag . $email;
}
}
+11 -8
View File
@@ -954,11 +954,10 @@ class Curl implements Interface\RequestsInterface
)
) {
unset($this->config['headers'][$header_key]);
unset($this->headers_named[$header_key]);
unset($this->headers_named[strtolower($header_key)]);
} elseif (
// string value, array keys = in
// or both array and not a full match in the one before
(is_string($value) || is_array($value)) &&
// right side is array, and value is anything string or array
// if right side is string, and we give value array, we do nothing
is_array($this->config['headers'][$header_key])
) {
// part remove of key, value must be array
@@ -966,10 +965,14 @@ class Curl implements Interface\RequestsInterface
$value = [$value];
}
// array values so we rewrite the key pos
$this->config['headers'][$header_key] = array_values(array_diff(
$this->config['headers'][$header_key],
$value
));
/** @var array<int|string,string> $headers */
$headers = $this->config['headers'][$header_key];
$this->config['headers'][$header_key] = array_values(
array_diff(
$headers,
$value
)
);
}
}
}
@@ -277,6 +277,38 @@ final class CoreLibsConvertJsonTest extends TestCase
);
}
/**
* Check if invalid flag throws exception
*
* @covers ::jsonValidate
* @testdox jsonValidate test exception thrown on invalid flag
*
* @return void
*/
public function testJsonValidateInvalidFlag(): void
{
$this->expectException("\InvalidArgumentException");
\CoreLibs\Convert\Json::jsonValidate('{"valid": "json"}', 5);
}
/**
* Check if valid flag throws no exception
*
* @covers ::jsonValidate
* @testdox jsonValidate test no exception on valid flag
*
* @return void
*/
public function testJsonValidateValidflag(): void
{
$this->assertTrue(
\CoreLibs\Convert\Json::jsonValidate('{"valid": "json"}', 0)
);
$this->assertTrue(
\CoreLibs\Convert\Json::jsonValidate('{"valid": "json"}', JSON_INVALID_UTF8_IGNORE)
);
}
/**
* Undocumented function
*
-1
View File
@@ -45,7 +45,6 @@ use CoreLibs\DB\Support\ConvertPlaceholder;
* Test class for DB\IO + DB\SQL\PgSQL
* This will only test the PgSQL parts
* @coversDefaultClass \CoreLibs\DB\IO
* @coversDefaultClass \CoreLibs\DB\SQL\PgSQL
* @testdox \CoreLibs\DB\IO method tests for SQL\PgSQL
*/
final class CoreLibsDBIOTest extends TestCase
@@ -463,7 +463,6 @@ final class CoreLibsDebugSupportTest extends TestCase
* @testWith ["vendor/phpunit/phpunit/src/Framework/TestCase.php:6434","phar:///home/clemens/.phive/phars/phpunit-9.6.13.phar/phpunit/Framework/TestCase.php:6434"]
* @testdox getCallerFileLine check based on regex .../Framework/TestCase.php:\d+ [$_dataName]
*
* @param string $expected
* @return void
*/
public function testGetCallerFileLine(): void
@@ -479,7 +478,8 @@ final class CoreLibsDebugSupportTest extends TestCase
. "\/Framework\/TestCase.php:\d+$/";
$this->assertMatchesRegularExpression(
$regex,
Support::getCallerFileLine()
Support::getCallerFileLine(),
'Failed for: ' . Support::getCallerFileLine()
);
}
+1
View File
@@ -0,0 +1 @@
test.env
+1 -1
View File
@@ -1,7 +1,7 @@
<?php
$set = 0;
foreach (['/../../www', '/../www', '/../..', '/..', '/../../src', '/../src'] as $src) {
foreach (['/../../www', '/../www', '/../../src', '/../src', '/../..', '/..'] as $src) {
if (is_file(dirname(__DIR__) . $src . '/vendor/autoload.php')) {
require dirname(__DIR__) . $src . '/vendor/autoload.php';
$set = 1;