Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 92581a6fd3 | |||
| 0f11fe9194 | |||
| 1b7c8ecd25 | |||
| de912d837e | |||
| d991a1d663 | |||
| e881854583 | |||
| 55aebb5373 | |||
| 6aa79dacc9 | |||
| c0236ee4d9 | |||
| 8c5ce49f70 | |||
| e935e0c6d3 | |||
| 5e972b7aec | |||
| b262b4be0f | |||
| 5cba8ca9ac | |||
| 397fcb25ff | |||
| 1e3ce162e5 | |||
| 22806d6add | |||
| 46e39fa85d | |||
| 8eb8e33dc4 | |||
| afcb953d2d | |||
| 742b4087aa | |||
| f0c8daf26a | |||
| ee32a90ff9 | |||
| 1823fd369b | |||
| 116f0a770c | |||
| b2123a183d | |||
| e92c585638 | |||
| 82c982c2c7 | |||
| 6b3ef52c5d | |||
| f2271313fc | |||
| a7f81766bf | |||
| 7a4c905ae3 | |||
| 0531b29749 | |||
| 5cf06f5b51 | |||
| bb23e760d0 | |||
| 45487b0039 | |||
| bfc82e12a5 | |||
| 8bd14b4385 | |||
| cfb6f14c09 | |||
| 969aa684d9 | |||
| 74999e6f6b | |||
| 787468e67c | |||
| 936b424065 | |||
| f765f50350 | |||
| 3b1f8745c2 | |||
| 9079d3120f | |||
| e945eac122 | |||
| 352e3dca1f | |||
| fb9e04fe55 | |||
| 1b46b378a3 | |||
| 4dda0937e5 | |||
| 935d5420a1 |
@@ -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,35 @@
|
||||
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 Composer.corelibs-composer-all
|
||||
if: github.event.repository.name != 'Composer.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-registry: "${{ secrets.OPJ_PACKAGE_USER }}|${{ secrets.OPJ_PACKAGE_API_KEY }}|${{ vars.OPJ_PACKAGE_URL }}"
|
||||
|
||||
call-check-and-build-remote:
|
||||
# call remote workflow if repository name is Composer.corelibs-composer-all
|
||||
if: github.event.repository.name == 'Composer.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-registry: "${{ secrets.OPJ_PACKAGE_USER }}|${{ secrets.OPJ_PACKAGE_API_KEY }}|${{ vars.OPJ_PACKAGE_URL }}"
|
||||
|
||||
@@ -0,0 +1,371 @@
|
||||
# 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-registry:
|
||||
required: false
|
||||
description: 'Package registry login infos: user|token|url[:user|token|url:...]'
|
||||
|
||||
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 }}
|
||||
opj-package-registry: ${{ steps.set-matrix.outputs.opj-package-registry }}
|
||||
steps:
|
||||
- name: Set matrix values
|
||||
id: set-matrix
|
||||
env:
|
||||
OPJ_PACKAGE_REGISTRY: ${{ secrets.opj-package-registry }}
|
||||
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
|
||||
# take the secrets opj-package-registry, split by ":" for each entry
|
||||
# then split by "|" for each entry to get user, token, url
|
||||
# then create a JSON array of objects with user, token, url
|
||||
if [ -n "$OPJ_PACKAGE_REGISTRY" ]; then
|
||||
echo "opj-package-registry=$(echo "$OPJ_PACKAGE_REGISTRY" | tr ':' '\n' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | jq -R 'split("|") | {user: .[0], token: .[1], url: .[2]}' | jq -cs .)" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "opj-package-registry=[]" >> $GITHUB_OUTPUT
|
||||
fi;
|
||||
|
||||
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
|
||||
- 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: Authenticate to private package repository
|
||||
env:
|
||||
OPJ_PACKAGE_REGISTRY: ${{ needs.setup.outputs.opj-package-registry }}
|
||||
run: |
|
||||
# opj-package-registry is a JSON array of objects with user, token, url
|
||||
for entry in $(echo "$OPJ_PACKAGE_REGISTRY" | jq -c '.[]'); do
|
||||
user=$(echo "$entry" | jq -r '.user')
|
||||
token=$(echo "$entry" | jq -r '.token')
|
||||
url=$(echo "$entry" | jq -r '.url')
|
||||
# if user, token or url is empty, skip this entry
|
||||
if [ -z "$user" ] || [ -z "$token" ] || [ -z "$url" ]; then
|
||||
echo "⚠️ Warning: Skipping invalid registry entry: $entry"
|
||||
continue
|
||||
fi
|
||||
echo "Authenticating to private package repository: $url with user: $user"
|
||||
composer config http-basic."$url" "$user" "$token"
|
||||
done
|
||||
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: |
|
||||
# if we are older than 12 there is no openclover
|
||||
# version can start with ^ and then have numbers, so we need to remove the ^ and then compare the version numbers
|
||||
phpunit_version="${{ matrix.phpunit-version }}"
|
||||
phpunit_version="${phpunit_version#^}"
|
||||
phpunit_version="${phpunit_version%%.*}"
|
||||
if [ "$phpunit_version" -lt 12 ]; then
|
||||
phpunit --colors=never \
|
||||
--no-coverage \
|
||||
--coverage-text=phpunit-coverage.txt \
|
||||
--coverage-clover=phpunit-openclover.xml \
|
||||
--log-junit=phpunit-junit.log
|
||||
else
|
||||
phpunit --colors=never \
|
||||
--no-coverage \
|
||||
--coverage-text=phpunit-coverage.txt \
|
||||
--only-summary-for-coverage-text \
|
||||
--coverage-openclover=phpunit-openclover.xml \
|
||||
--log-junit=phpunit-junit.log \
|
||||
--log-otr=phpunit-otr.log
|
||||
fi;
|
||||
|
||||
- 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 "$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
|
||||
|
||||
if [ -f phpunit-junit.log ]; then
|
||||
{
|
||||
echo "<h3>PHPUnit Test Results</h3>"
|
||||
echo ""
|
||||
# Extract totals from the top-level testsuite element (first testsuite line)
|
||||
JUNIT_LINE=$(grep -m1 '<testsuite ' phpunit-junit.log)
|
||||
TESTS=$(echo "$JUNIT_LINE" | grep -oP 'tests="\K[0-9]+')
|
||||
ASSERTIONS=$(echo "$JUNIT_LINE" | grep -oP 'assertions="\K[0-9]+')
|
||||
ERRORS=$(echo "$JUNIT_LINE" | grep -oP 'errors="\K[0-9]+')
|
||||
FAILURES=$(echo "$JUNIT_LINE" | grep -oP 'failures="\K[0-9]+')
|
||||
SKIPPED=$(echo "$JUNIT_LINE" | grep -oP 'skipped="\K[0-9]+')
|
||||
TIME=$(echo "$JUNIT_LINE" | grep -oP 'time="\K[0-9]+\.[0-9]+')
|
||||
PASSED=$(( TESTS - ERRORS - FAILURES - SKIPPED ))
|
||||
test_icon() {
|
||||
if [ "$1" -gt 0 ]; then echo "❌"; else echo "✅"; fi
|
||||
}
|
||||
echo "| Metric | Value |"
|
||||
echo "| --- | --- |"
|
||||
echo "| Tests | $TESTS |"
|
||||
echo "| Passed | $PASSED |"
|
||||
echo "| Assertions | $ASSERTIONS |"
|
||||
echo "| Skipped | $(test_icon "${SKIPPED:-0}") ${SKIPPED:-0} |"
|
||||
echo "| Failures | $(test_icon "${FAILURES:-0}") ${FAILURES:-0} |"
|
||||
echo "| Errors | $(test_icon "${ERRORS:-0}") ${ERRORS:-0} |"
|
||||
echo "| Time | ${TIME}s |"
|
||||
echo ""
|
||||
} >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
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
|
||||
|
||||
if [ -f phpunit-openclover.xml ]; then
|
||||
if [ -f code-coverage-summary.md ]; then
|
||||
cat code-coverage-summary.md >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
if [ -f code-coverage-details.md ]; then
|
||||
cat code-coverage-details.md >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
fi
|
||||
|
||||
+4
-2
@@ -54,8 +54,10 @@ 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,
|
||||
|
||||
// If enabled, missing properties will be created when
|
||||
// they are first seen. If false, we'll report an
|
||||
|
||||
+6
-6
@@ -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.19" location="./tools/phpunit" copy="false"/>
|
||||
<phar name="phpcs" version="^3.7.2" installed="3.10.0" location="./tools/phpcs" copy="false"/>
|
||||
<phar name="phpcbf" version="^3.7.2" installed="3.10.0" location="./tools/phpcbf" copy="false"/>
|
||||
<phar name="psalm" version="^5.15.0" installed="5.24.0" location="./tools/psalm" copy="false"/>
|
||||
<phar name="phpstan" version="^1.10.37" installed="1.11.1" location="./tools/phpstan" copy="false"/>
|
||||
<phar name="phan" version="^5.4.2" installed="5.4.3" location="./tools/phan" 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.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"
|
||||
},
|
||||
...
|
||||
+10
-10
@@ -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"
|
||||
}]
|
||||
}
|
||||
|
||||
+16
-1
@@ -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,18 @@
|
||||
<directory>test/phpunit</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<coverage processUncoveredFiles="true">
|
||||
<include>
|
||||
<directory suffix=".php">./src</directory>
|
||||
</include>
|
||||
<!-- <report>
|
||||
<html outputDirectory="build/coverage-report"/>
|
||||
</report> -->
|
||||
</coverage>
|
||||
<logging>
|
||||
<!-- JUnit XML log (Most Common for CI/CD) -->
|
||||
<junit outputFile="build/logs/junit.xml"/>
|
||||
<!-- TestDox HTML log -->
|
||||
<testdoxHtml outputFile="build/testdox.html"/>
|
||||
</logging>
|
||||
</phpunit>
|
||||
|
||||
@@ -1 +1 @@
|
||||
9.33.0
|
||||
9.41.3
|
||||
|
||||
+106
-51
@@ -3,23 +3,110 @@
|
||||
BASE_FOLDER=$(dirname "$(readlink -f "$0")")"/";
|
||||
PACKAGE_DOWNLOAD="${BASE_FOLDER}package-download/";
|
||||
if [ ! -d "${PACKAGE_DOWNLOAD}" ]; then
|
||||
mkdir "${PACKAGE_DOWNLOAD}";
|
||||
mkdir "${PACKAGE_DOWNLOAD}";
|
||||
fi;
|
||||
VERSION=$(git tag --list | sort -V | tail -n1 | sed -e "s/^v//");
|
||||
file_last_published="${BASE_FOLDER}last.published";
|
||||
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}"
|
||||
_GITEA_URL_PUSH="${4}"
|
||||
_GITEA_USER="${5}"
|
||||
_GITEA_TOKEN="${6}"
|
||||
_PACKAGE_DOWNLOAD="${7}"
|
||||
_VERSION="${8}"
|
||||
_file_last_published="${9}"
|
||||
|
||||
if [ -z "${_GITEA_PUBLISH}" ]; then
|
||||
return
|
||||
fi;
|
||||
if [ -n "${_GITEA_UPLOAD_FILENAME}" ] &&
|
||||
[ -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";
|
||||
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 [ $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";
|
||||
echo "${_VERSION}" > "${_file_last_published}";
|
||||
fi;
|
||||
fi;
|
||||
fi;
|
||||
else
|
||||
echo "[!] Missing either GITEA_UPLOAD_FILENAME, GITEA_URL_DL, GITEA_URL_PUSH, GITEA_USER or GITEA_TOKEN environment variable";
|
||||
fi;
|
||||
}
|
||||
|
||||
function gitlab_publish
|
||||
{
|
||||
_GITLAB_PUBLISH="${1}";
|
||||
_GITLAB_URL="${2}";
|
||||
_GITLAB_DEPLOY_TOKEN="${3}";
|
||||
_PACKAGE_DOWNLOAD="${4}"
|
||||
_VERSION="${5}"
|
||||
_file_last_published="${6}"
|
||||
if [ -z "${_GITLAB_PUBLISH}" ]; then
|
||||
return;
|
||||
fi;
|
||||
if [ -n "${_GITLAB_URL}" ] && [ -n "${_GITLAB_DEPLOY_TOKEN}" ]; then
|
||||
curl --data tag=v"${_VERSION}" \
|
||||
--header "Deploy-Token: ${_GITLAB_DEPLOY_TOKEN}" \
|
||||
"${_GITLAB_URL}";
|
||||
curl --data branch=master \
|
||||
--header "Deploy-Token: ${_GITLAB_DEPLOY_TOKEN}" \
|
||||
"${_GITLAB_URL}";
|
||||
echo "${_VERSION}" > "${_file_last_published}";
|
||||
else
|
||||
echo "[!] Missing GITLAB_URL or GITLAB_DEPLOY_TOKEN environment variable";
|
||||
fi;
|
||||
}
|
||||
|
||||
|
||||
if [ -z "${VERSION}" ]; then
|
||||
echo "Version must be set in the form x.y.z without any leading characters";
|
||||
exit;
|
||||
echo "[!] Version must be set in the form x.y.z without any leading characters";
|
||||
exit;
|
||||
fi;
|
||||
# compare version, if different or newer, deploy
|
||||
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;
|
||||
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;
|
||||
|
||||
# read in the .env.deploy file and we must have
|
||||
@@ -36,62 +123,30 @@ fi;
|
||||
# GITLAB_TOKEN
|
||||
# GITLAB_URL
|
||||
if [ ! -f "${BASE_FOLDER}.env.deploy" ]; then
|
||||
echo "Deploy enviroment file .env.deploy is missing";
|
||||
exit;
|
||||
echo "[!] Deploy enviroment file .env.deploy is missing";
|
||||
exit;
|
||||
fi;
|
||||
set -o allexport;
|
||||
cd "${BASE_FOLDER}" || exit;
|
||||
cd "${BASE_FOLDER}" || exit
|
||||
# shellcheck source=.env.deploy
|
||||
source .env.deploy;
|
||||
cd - || exit;
|
||||
cd - >/dev/null 2>&1 || exit;
|
||||
set +o allexport;
|
||||
|
||||
if [ "${go_flag}" != "go" ]; then
|
||||
echo "No go flag given";
|
||||
echo "Would publish ${VERSION}";
|
||||
echo "[END]";
|
||||
exit;
|
||||
echo "[!] No go flag given";
|
||||
echo "> Would publish ${VERSION}";
|
||||
echo "[END]";
|
||||
exit;
|
||||
fi;
|
||||
|
||||
echo "[START]";
|
||||
# gitea
|
||||
# skip iof
|
||||
if [ -n "${GITEA_PUBLISH}" ]; then
|
||||
if [ -n "${GITEA_UPLOAD_FILENAME}" ] &&
|
||||
[ -n "${GITEA_URL_DL}" ] && [ -n "${GITEA_URL_PUSH}" ] &&
|
||||
[ -n "${GITEA_USER}" ] && [ -n "${GITEA_TOKEN}" ]; then
|
||||
if [ ! -f "${PACKAGE_DOWNLOAD}${GITEA_UPLOAD_FILENAME}-v${VERSION}.zip" ]; then
|
||||
curl -LJO \
|
||||
--output-dir "${PACKAGE_DOWNLOAD}" \
|
||||
"${GITEA_URL_DL}"/v"${VERSION}".zip;
|
||||
fi;
|
||||
if [ ! -f "${PACKAGE_DOWNLOAD}${GITEA_UPLOAD_FILENAME}-v${VERSION}.zip" ]; then
|
||||
echo "Version file does not exist for ${VERSION}";
|
||||
else
|
||||
curl --user "${GITEA_USER}":"${GITEA_TOKEN}" \
|
||||
--upload-file "${PACKAGE_DOWNLOAD}${GITEA_UPLOAD_FILENAME}-v${VERSION}.zip" \
|
||||
"${GITEA_URL_PUSH}"?version="${VERSION}";
|
||||
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";
|
||||
fi;
|
||||
fi;
|
||||
gitea_publish "${GITEA_PUBLISH}" "${GITEA_UPLOAD_FILENAME}" "${GITEA_URL_DL}" "${GITEA_URL_PUSH}" "${GITEA_USER}" "${GITEA_TOKEN}" "${PACKAGE_DOWNLOAD}" "${VERSION}" "${file_last_published}";
|
||||
gitea_publish "${PR_GITEA_PUBLISH}" "${PR_GITEA_UPLOAD_FILENAME}" "${PR_GITEA_URL_DL}" "${PR_GITEA_URL_PUSH}" "${PR_GITEA_USER}" "${PR_GITEA_TOKEN}" "${PACKAGE_DOWNLOAD}" "${VERSION}" "${file_last_published}";
|
||||
|
||||
# gitlab
|
||||
if [ -n "${GITLAB_PUBLISH}" ]; then
|
||||
if [ -n "${GITLAB_URL}" ] && [ -n "${GITLAB_DEPLOY_TOKEN}" ]; then
|
||||
curl --data tag=v"${VERSION}" \
|
||||
--header "Deploy-Token: ${GITLAB_DEPLOY_TOKEN}" \
|
||||
"${GITLAB_URL}";
|
||||
curl --data branch=master \
|
||||
--header "Deploy-Token: ${GITLAB_DEPLOY_TOKEN}" \
|
||||
"${GITLAB_URL}";
|
||||
echo "${VERSION}" > "${file_last_published}";
|
||||
else
|
||||
echo "Missing GITLAB_URL or GITLAB_DEPLOY_TOKEN environment variable";
|
||||
fi;
|
||||
fi;
|
||||
# gitlab_publish "${GITLAB_PUBLISH}" "${GITLAB_URL}" "${GITLAB_DEPLOY_TOKEN}" "${PACKAGE_DOWNLOAD}" "${VERSION}" "${file_last_published}";
|
||||
echo "";
|
||||
echo "[DONE]";
|
||||
|
||||
|
||||
+8
-4
@@ -10,12 +10,16 @@ class Email
|
||||
/** @var array<int,string> */
|
||||
private static array $email_regex_check = [
|
||||
0 => "^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@"
|
||||
. "[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]{1,})*\.([a-zA-Z]{2,}){1}$", // MASTER
|
||||
// . "[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]{1,})*\.([a-zA-Z]{2,}){1}$", // MASTER
|
||||
// fixed pattern matching for domain
|
||||
. "(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$", // MASTER
|
||||
1 => "@(.*)@(.*)", // double @
|
||||
2 => "^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@", // wrong part before @
|
||||
3 => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.([a-zA-Z]{2,}){1}$", // wrong part after @
|
||||
4 => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.", // wrong domain name part
|
||||
5 => "\.([a-zA-Z]{2,6}){1}$", // wrong top level part
|
||||
// 3 => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.([a-zA-Z]{2,}){1}$", // wrong part after @
|
||||
3 => "@(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$", // wrong part after @
|
||||
// 4 => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.", // wrong domain name part
|
||||
4 => "@(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.", // wrong domain name part
|
||||
5 => "\.[a-zA-Z]{2,6}$", // wrong top level part
|
||||
6 => "@(.*)\.{2,}", // double .. in domain name part
|
||||
7 => "@.*\.$" // ends with a dot, top level, domain missing
|
||||
];
|
||||
|
||||
@@ -114,7 +114,7 @@ class Encoding
|
||||
(($char != $r_char && (!self::$mb_error_char ||
|
||||
in_array(self::$mb_error_char, ['none', 'long', 'entity']))) ||
|
||||
($char != $r_char && $r_char == self::$mb_error_char && self::$mb_error_char)) &&
|
||||
ord($char) != 194
|
||||
ord($char[0]) != 194
|
||||
) {
|
||||
$failed[] = $char;
|
||||
}
|
||||
|
||||
+9
-3
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -150,14 +150,14 @@ class ArrayHandler
|
||||
* array search simple. looks for key, value combination, if found, returns true
|
||||
* on default does not strict check, so string '4' will match int 4 and vica versa
|
||||
*
|
||||
* @param array<mixed> $array search in as array
|
||||
* @param array<mixed> $in_array search in as array
|
||||
* @param string|int $key key (key to search in)
|
||||
* @param string|int|bool|array<string|int|bool> $value values list (what to find)
|
||||
* @param bool $strict [false], if set to true, will strict check key/value
|
||||
* @return bool true on found, false on not found
|
||||
*/
|
||||
public static function arraySearchSimple(
|
||||
array $array,
|
||||
array $in_array,
|
||||
string|int $key,
|
||||
string|int|bool|array $value,
|
||||
bool $strict = false
|
||||
@@ -166,7 +166,7 @@ class ArrayHandler
|
||||
if (!is_array($value)) {
|
||||
$value = [$value];
|
||||
}
|
||||
foreach ($array as $_key => $_value) {
|
||||
foreach ($in_array as $_key => $_value) {
|
||||
// if value is an array, we search
|
||||
if (is_array($_value)) {
|
||||
// call recursive, and return result if it is true, else continue
|
||||
@@ -189,19 +189,19 @@ class ArrayHandler
|
||||
* If prefix is turned on each found group will be prefixed with the
|
||||
* search key
|
||||
*
|
||||
* @param array<mixed> $array array to search in
|
||||
* @param array<mixed> $in_array array to search in
|
||||
* @param array<mixed> $needles keys to find in array
|
||||
* @param bool $flat [false] Turn on flat output
|
||||
* @param bool $prefix [false] Prefix found with needle key
|
||||
* @return array<mixed> Found values
|
||||
*/
|
||||
public static function arraySearchKey(
|
||||
array $array,
|
||||
array $in_array,
|
||||
array $needles,
|
||||
bool $flat = false,
|
||||
bool $prefix = false
|
||||
): array {
|
||||
$iterator = new \RecursiveArrayIterator($array);
|
||||
$iterator = new \RecursiveArrayIterator($in_array);
|
||||
$recursive = new \RecursiveIteratorIterator(
|
||||
$iterator,
|
||||
\RecursiveIteratorIterator::SELF_FIRST
|
||||
@@ -248,7 +248,7 @@ class ArrayHandler
|
||||
* If not found return an array with the array block there the required key is missing,
|
||||
* the path as string with seperator block set and the missing key entry
|
||||
*
|
||||
* @param array<mixed> $array
|
||||
* @param array<mixed> $in_array
|
||||
* @param string|int|float|bool $search_value
|
||||
* @param string|array<string> $required_key
|
||||
* @param ?string $search_key [null]
|
||||
@@ -257,7 +257,7 @@ class ArrayHandler
|
||||
* @return array<array{content?:array<mixed>,path?:string,missing_key?:array<string>}>
|
||||
*/
|
||||
public static function findArraysMissingKey(
|
||||
array $array,
|
||||
array $in_array,
|
||||
string|int|float|bool $search_value,
|
||||
string|array $required_key,
|
||||
?string $search_key = null,
|
||||
@@ -265,7 +265,7 @@ class ArrayHandler
|
||||
string $current_path = ''
|
||||
): array {
|
||||
$results = [];
|
||||
foreach ($array as $key => $value) {
|
||||
foreach ($in_array as $key => $value) {
|
||||
$path = $current_path ? $current_path . $path_separator . $key : $key;
|
||||
|
||||
if (is_array($value)) {
|
||||
@@ -321,7 +321,7 @@ class ArrayHandler
|
||||
* Find key => value entry and return set with key for all matching
|
||||
* Can search recursively through nested arrays if recursive flag is set
|
||||
*
|
||||
* @param array<mixed> $array
|
||||
* @param array<mixed> $in_array
|
||||
* @param string $lookup
|
||||
* @param int|string|float|bool $search
|
||||
* @param bool $strict [false]
|
||||
@@ -332,7 +332,7 @@ class ArrayHandler
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public static function selectArrayFromOption(
|
||||
array $array,
|
||||
array $in_array,
|
||||
string $lookup,
|
||||
int|string|float|bool $search,
|
||||
bool $strict = false,
|
||||
@@ -342,7 +342,7 @@ class ArrayHandler
|
||||
string $flat_separator = self::DATA_SEPARATOR
|
||||
): array {
|
||||
// skip on empty
|
||||
if ($array == []) {
|
||||
if ($in_array == []) {
|
||||
return [];
|
||||
}
|
||||
// init return result
|
||||
@@ -352,7 +352,7 @@ class ArrayHandler
|
||||
$search = strtolower($search);
|
||||
}
|
||||
|
||||
foreach ($array as $key => $value) {
|
||||
foreach ($in_array as $key => $value) {
|
||||
// Handle current level search
|
||||
if (isset($value[$lookup])) {
|
||||
$compareValue = $value[$lookup];
|
||||
@@ -399,14 +399,14 @@ class ArrayHandler
|
||||
/**
|
||||
* main wrapper function for next/prev key
|
||||
*
|
||||
* @param array<mixed> $array array to search in
|
||||
* @param array<mixed> $in_array array to search in
|
||||
* @param int|string $key key for next/prev
|
||||
* @param bool $next [=true] if to search next or prev
|
||||
* @return int|string|null Next/prev key or null for end/first
|
||||
*/
|
||||
private static function arrayGetKey(array $array, int|string $key, bool $next = true): int|string|null
|
||||
private static function arrayGetKey(array $in_array, int|string $key, bool $next = true): int|string|null
|
||||
{
|
||||
$keys = array_keys($array);
|
||||
$keys = array_keys($in_array);
|
||||
if (($position = array_search($key, $keys, true)) === false) {
|
||||
return null;
|
||||
}
|
||||
@@ -422,26 +422,26 @@ class ArrayHandler
|
||||
* Get previous array key from an array
|
||||
* null on not found
|
||||
*
|
||||
* @param array<mixed> $array
|
||||
* @param array<mixed> $in_array
|
||||
* @param int|string $key
|
||||
* @return int|string|null Next key, or null for not found
|
||||
*/
|
||||
public static function arrayGetPrevKey(array $array, int|string $key): int|string|null
|
||||
public static function arrayGetPrevKey(array $in_array, int|string $key): int|string|null
|
||||
{
|
||||
return self::arrayGetKey($array, $key, false);
|
||||
return self::arrayGetKey($in_array, $key, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next array key from an array
|
||||
* null on not found
|
||||
*
|
||||
* @param array<mixed> $array
|
||||
* @param array<mixed> $in_array
|
||||
* @param int|string $key
|
||||
* @return int|string|null Next key, or null for not found
|
||||
*/
|
||||
public static function arrayGetNextKey(array $array, int|string $key): int|string|null
|
||||
public static function arrayGetNextKey(array $in_array, int|string $key): int|string|null
|
||||
{
|
||||
return self::arrayGetKey($array, $key, true);
|
||||
return self::arrayGetKey($in_array, $key, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -463,27 +463,27 @@ class ArrayHandler
|
||||
}
|
||||
// default key is not string
|
||||
$key_is_string = false;
|
||||
$arrays = func_get_args();
|
||||
$in_arrays = func_get_args();
|
||||
// if last is not array, then assume it is trigger for key is always string
|
||||
if (!is_array(end($arrays))) {
|
||||
if (array_pop($arrays)) {
|
||||
if (!is_array(end($in_arrays))) {
|
||||
if (array_pop($in_arrays)) {
|
||||
$key_is_string = true;
|
||||
}
|
||||
}
|
||||
// check that arrays count is at least two, else we don't have enough to do anything
|
||||
if (count($arrays) < 2) {
|
||||
if (count($in_arrays) < 2) {
|
||||
throw new \ArgumentCountError(__FUNCTION__ . ' needs two or more array arguments');
|
||||
}
|
||||
$merged = [];
|
||||
while ($arrays) {
|
||||
$array = array_shift($arrays);
|
||||
if (!is_array($array)) {
|
||||
while ($in_arrays) {
|
||||
$in_array = array_shift($in_arrays);
|
||||
if (!is_array($in_array)) {
|
||||
throw new \TypeError(__FUNCTION__ . ' encountered a non array argument');
|
||||
}
|
||||
if (!$array) {
|
||||
if (!$in_array) {
|
||||
continue;
|
||||
}
|
||||
foreach ($array as $key => $value) {
|
||||
foreach ($in_array as $key => $value) {
|
||||
// if string or if key is assumed to be string do key match
|
||||
// else add new entry
|
||||
if (is_string($key) || $key_is_string === false) {
|
||||
@@ -589,14 +589,14 @@ class ArrayHandler
|
||||
* converts multi dimensional array to a flat array
|
||||
* does NOT preserve keys
|
||||
*
|
||||
* @param array<mixed> $array multi dimensionial array
|
||||
* @param array<mixed> $in_array multi dimensionial array
|
||||
* @return array<mixed> flattened array
|
||||
*/
|
||||
public static function flattenArray(array $array): array
|
||||
public static function flattenArray(array $in_array): array
|
||||
{
|
||||
$return = [];
|
||||
array_walk_recursive(
|
||||
$array,
|
||||
$in_array,
|
||||
function ($value) use (&$return) {
|
||||
$return[] = $value;
|
||||
}
|
||||
@@ -607,13 +607,13 @@ class ArrayHandler
|
||||
/**
|
||||
* will loop through an array recursivly and write the array keys back
|
||||
*
|
||||
* @param array<mixed> $array multidemnsional array to flatten
|
||||
* @param array<mixed> $in_array multidemnsional array to flatten
|
||||
* @param array<mixed> $return recoursive pass on array of keys
|
||||
* @return array<mixed> flattened keys array
|
||||
*/
|
||||
public static function flattenArrayKey(array $array, array $return = []): array
|
||||
public static function flattenArrayKey(array $in_array, array $return = []): array
|
||||
{
|
||||
foreach ($array as $key => $sub) {
|
||||
foreach ($in_array as $key => $sub) {
|
||||
$return[] = $key;
|
||||
if (is_array($sub) && count($sub) > 0) {
|
||||
$return = self::flattenArrayKey($sub, $return);
|
||||
@@ -626,14 +626,14 @@ class ArrayHandler
|
||||
* as above will flatten an array, but in this case only the outmost
|
||||
* leave nodes, all other keyswill be skipped
|
||||
*
|
||||
* @param array<mixed> $array multidemnsional array to flatten
|
||||
* @param array<mixed> $in_array multidemnsional array to flatten
|
||||
* @return array<mixed> flattened keys array
|
||||
*/
|
||||
public static function flattenArrayKeyLeavesOnly(array $array): array
|
||||
public static function flattenArrayKeyLeavesOnly(array $in_array): array
|
||||
{
|
||||
$return = [];
|
||||
array_walk_recursive(
|
||||
$array,
|
||||
$in_array,
|
||||
function ($value, $key) use (&$return) {
|
||||
$return[] = $key;
|
||||
}
|
||||
@@ -645,14 +645,14 @@ class ArrayHandler
|
||||
* searches for key -> value in an array tree and writes the value one level up
|
||||
* this will remove this leaf will all other values
|
||||
*
|
||||
* @param array<mixed> $array nested array
|
||||
* @param array<mixed> $in_array nested array
|
||||
* @param string|int $search key to find that has no sub leaf
|
||||
* and will be pushed up
|
||||
* @return array<mixed> modified, flattened array
|
||||
*/
|
||||
public static function arrayFlatForKey(array $array, string|int $search): array
|
||||
public static function arrayFlatForKey(array $in_array, string|int $search): array
|
||||
{
|
||||
foreach ($array as $key => $value) {
|
||||
foreach ($in_array as $key => $value) {
|
||||
// if it is not an array do just nothing
|
||||
if (!is_array($value)) {
|
||||
continue;
|
||||
@@ -660,14 +660,14 @@ class ArrayHandler
|
||||
// probe it has search key
|
||||
if (isset($value[$search])) {
|
||||
// set as current
|
||||
$array[$key] = $value[$search];
|
||||
$in_array[$key] = $value[$search];
|
||||
} else {
|
||||
// call up next node down
|
||||
// $array[$key] = call_user_func(__METHOD__, $value, $search);
|
||||
$array[$key] = self::arrayFlatForKey($value, $search);
|
||||
// $in_array[$key] = call_user_func(__METHOD__, $value, $search);
|
||||
$in_array[$key] = self::arrayFlatForKey($value, $search);
|
||||
}
|
||||
}
|
||||
return $array;
|
||||
return $in_array;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -677,13 +677,13 @@ class ArrayHandler
|
||||
*
|
||||
* https://stackoverflow.com/a/369608
|
||||
*
|
||||
* @param array<mixed> $array Array where elements are located
|
||||
* @param array<mixed> $in_array Array where elements are located
|
||||
* @param array<mixed> $remove Elements to remove
|
||||
* @return array<mixed> Array with $remove elements removed
|
||||
*/
|
||||
public static function arrayRemoveEntry(array $array, array $remove): array
|
||||
public static function arrayRemoveEntry(array $in_array, array $remove): array
|
||||
{
|
||||
return array_diff($array, $remove);
|
||||
return array_diff($in_array, $remove);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -693,20 +693,20 @@ class ArrayHandler
|
||||
* key list is a list[string]
|
||||
* if key list is empty, return array as is
|
||||
*
|
||||
* @param array<string,mixed> $array
|
||||
* @param array<string,mixed> $in_array
|
||||
* @param array<string> $key_list
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
public static function arrayReturnMatchingKeyOnly(
|
||||
array $array,
|
||||
array $in_array,
|
||||
array $key_list
|
||||
): array {
|
||||
// on empty return as is
|
||||
if (empty($key_list)) {
|
||||
return $array;
|
||||
return $in_array;
|
||||
}
|
||||
return array_filter(
|
||||
$array,
|
||||
$in_array,
|
||||
fn($key) => in_array($key, $key_list),
|
||||
ARRAY_FILTER_USE_KEY
|
||||
);
|
||||
@@ -748,16 +748,19 @@ class ArrayHandler
|
||||
* sort ascending or descending with or without lower case convert
|
||||
* value only, will loose key connections unless preserve_keys is set to true
|
||||
*
|
||||
* @param array<mixed> $array array to sort by values
|
||||
* @param int $params sort flags
|
||||
* @param array<mixed> $in_array Array to sort by values
|
||||
* @param bool $case_insensitive [false] Sort case insensitive
|
||||
* @param bool $reverse [false] Reverse sort
|
||||
* @param bool $maintain_keys [false] Maintain keys
|
||||
* @param int $flag [SORT_REGULAR] Sort flags
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public static function sortArray(
|
||||
array $array,
|
||||
array $in_array,
|
||||
bool $case_insensitive = false,
|
||||
bool $reverse = false,
|
||||
bool $maintain_keys = false,
|
||||
int $params = SORT_REGULAR
|
||||
int $flag = SORT_REGULAR
|
||||
): array {
|
||||
$fk_sort_lower_case = function (string $a, string $b): int {
|
||||
return strtolower($a) <=> strtolower($b);
|
||||
@@ -767,26 +770,26 @@ class ArrayHandler
|
||||
};
|
||||
$case_insensitive ? (
|
||||
$maintain_keys ?
|
||||
(uasort($array, $reverse ? $fk_sort_lower_case_reverse : $fk_sort_lower_case)) :
|
||||
(usort($array, $reverse ? $fk_sort_lower_case_reverse : $fk_sort_lower_case))
|
||||
(uasort($in_array, $reverse ? $fk_sort_lower_case_reverse : $fk_sort_lower_case)) :
|
||||
(usort($in_array, $reverse ? $fk_sort_lower_case_reverse : $fk_sort_lower_case))
|
||||
) :
|
||||
(
|
||||
$maintain_keys ?
|
||||
($reverse ? arsort($array, $params) : asort($array, $params)) :
|
||||
($reverse ? rsort($array, $params) : sort($array, $params))
|
||||
($reverse ? arsort($in_array, $flag) : asort($in_array, $flag)) :
|
||||
($reverse ? rsort($in_array, $flag) : sort($in_array, $flag))
|
||||
);
|
||||
return $array;
|
||||
return $in_array;
|
||||
}
|
||||
|
||||
/**
|
||||
* sort by key ascending or descending and return
|
||||
*
|
||||
* @param array<mixed> $array
|
||||
* @param bool $case_insensitive [false]
|
||||
* @param bool $reverse [false]
|
||||
* @param array<mixed> $in_array Array to srt
|
||||
* @param bool $case_insensitive [false] Sort keys case insenstive
|
||||
* @param bool $reverse [false] Reverse key sort
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public static function ksortArray(array $array, bool $case_insensitive = false, bool $reverse = false): array
|
||||
public static function ksortArray(array $in_array, bool $case_insensitive = false, bool $reverse = false): array
|
||||
{
|
||||
$fk_sort_lower_case = function (string $a, string $b): int {
|
||||
return strtolower($a) <=> strtolower($b);
|
||||
@@ -801,12 +804,12 @@ class ArrayHandler
|
||||
return $b <=> $a;
|
||||
};
|
||||
uksort(
|
||||
$array,
|
||||
$in_array,
|
||||
$case_insensitive ?
|
||||
($reverse ? $fk_sort_lower_case_reverse : $fk_sort_lower_case) :
|
||||
($reverse ? $fk_sort_reverse : $fk_sort)
|
||||
);
|
||||
return $array;
|
||||
return $in_array;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+50
-21
@@ -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) ? ' ' : '');
|
||||
@@ -395,39 +395,68 @@ class DateTime
|
||||
* does a reverse of the timeStringFormat and converts the string from
|
||||
* xd xh xm xs xms to a timestamp.microtime format
|
||||
*
|
||||
* @param string|int|float $timestring formatted interval
|
||||
* @return string|int|float converted float interval, or string as is
|
||||
* @param string|int|float $timestring formatted interval
|
||||
* @param bool $throw_exception [default=false] if set to true will throw exception
|
||||
* instead of returning input value as is
|
||||
* @return string|int|float converted float interval, or string as is
|
||||
*/
|
||||
public static function stringToTime(string|int|float $timestring): string|int|float
|
||||
{
|
||||
public static function stringToTime(
|
||||
string|int|float $timestring,
|
||||
bool $throw_exception = false
|
||||
): string|int|float {
|
||||
$timestamp = 0;
|
||||
if (!preg_match("/(d|h|m|s|ms)/", (string)$timestring)) {
|
||||
return $timestring;
|
||||
}
|
||||
$timestring = (string)$timestring;
|
||||
// pos for preg match read + multiply factor
|
||||
$timegroups = [2 => 86400, 4 => 3600, 6 => 60, 8 => 1];
|
||||
$matches = [];
|
||||
// if start with -, strip and set negative
|
||||
$negative = false;
|
||||
if (preg_match("/^-/", $timestring)) {
|
||||
$negative = true;
|
||||
$timestring = substr($timestring, 1);
|
||||
}
|
||||
// preg match: 0: full string
|
||||
// 2, 4, 6, 8 are the to need values
|
||||
preg_match("/^((\d+)d ?)?((\d+)h ?)?((\d+)m ?)?((\d+)s ?)?((\d+)ms)?$/", $timestring, $matches);
|
||||
if (
|
||||
!preg_match(
|
||||
"/^\s*(-)?\s*"
|
||||
. "((\d+)\s*d(?:ay(?:s)?)?)?\s*"
|
||||
. "((\d+)\s*h(?:our(?:s)?)?)?\s*"
|
||||
. "((\d+)\s*m(?:in(?:ute)?(?:s)?)?)?\s*"
|
||||
. "((\d+)\s*s(?:ec(?:ond)?(?:s)?)?)?\s*"
|
||||
. "((\d+)\s*m(?:illi)?s(?:ec(?:ond)?(?:s)?)?)?\s*"
|
||||
. "$/",
|
||||
(string)$timestring,
|
||||
$matches
|
||||
)
|
||||
) {
|
||||
if ($throw_exception) {
|
||||
throw new \InvalidArgumentException(
|
||||
'Invalid time string format, cannot parse: "' . (string)$timestring . '"',
|
||||
1
|
||||
);
|
||||
}
|
||||
return $timestring;
|
||||
}
|
||||
if (count($matches) < 2) {
|
||||
if ($throw_exception) {
|
||||
throw new \InvalidArgumentException(
|
||||
'Invalid time string format, no interval value found: "' . (string)$timestring . '"',
|
||||
2
|
||||
);
|
||||
}
|
||||
return $timestring;
|
||||
}
|
||||
// pos for preg match read + multiply factor
|
||||
$timegroups = [3 => 86400, 5 => 3600, 7 => 60, 9 => 1];
|
||||
// if start with -, strip and set negative
|
||||
$negative = false;
|
||||
if (!empty($matches[1])) {
|
||||
$negative = true;
|
||||
}
|
||||
// multiply the returned matches and sum them up. the last one (ms) is added with .
|
||||
foreach ($timegroups as $i => $time_multiply) {
|
||||
if (isset($matches[$i]) && is_numeric($matches[$i])) {
|
||||
$timestamp += (float)$matches[$i] * $time_multiply;
|
||||
}
|
||||
}
|
||||
if (isset($matches[10]) && is_numeric($matches[10])) {
|
||||
$timestamp .= '.' . $matches[10];
|
||||
if (isset($matches[11]) && is_numeric($matches[11])) {
|
||||
// for milliseconds, we need to divide by 1000 and add them
|
||||
$timestamp += ((float)$matches[11] / 1000);
|
||||
}
|
||||
if ($negative) {
|
||||
// cast to flaot so we can do a negative multiplication
|
||||
// cast to float so we can do a negative multiplication
|
||||
$timestamp = (float)$timestamp * -1;
|
||||
}
|
||||
return $timestamp;
|
||||
|
||||
+113
-66
@@ -14,6 +14,7 @@ class Byte
|
||||
public const BYTE_FORMAT_NOSPACE = 1;
|
||||
public const BYTE_FORMAT_ADJUST = 2;
|
||||
public const BYTE_FORMAT_SI = 4;
|
||||
public const RETURN_AS_STRING = 8;
|
||||
|
||||
/**
|
||||
* This function replaces the old byteStringFormat
|
||||
@@ -42,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 = 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,7 +119,9 @@ class Byte
|
||||
* @param int $flags bitwise flag with use space turned on
|
||||
* BYTE_FORMAT_SI: use 1000 instead of 1024
|
||||
* @return string|int|float converted value or original value
|
||||
* @throws \InvalidArgumentException 1: no valid flag set
|
||||
* @throws \InvalidArgumentException no valid flag set
|
||||
* @throws \LengthException number too large to convert to int
|
||||
* @throws \RuntimeException BCMath extension not loaded if flag is set to string
|
||||
*/
|
||||
public static function stringByteFormat(string|int|float $number, int $flags = 0): string|int|float
|
||||
{
|
||||
@@ -129,7 +131,12 @@ class Byte
|
||||
} else {
|
||||
$si = false;
|
||||
}
|
||||
if ($flags != 0 && $flags != 4) {
|
||||
if ($flags & self::RETURN_AS_STRING) {
|
||||
$return_as_string = true;
|
||||
} else {
|
||||
$return_as_string = false;
|
||||
}
|
||||
if ($flags != 0 && $flags != 4 && $flags != 8 && $flags != 12) {
|
||||
throw new \InvalidArgumentException("Invalid flags parameter: $flags", 1);
|
||||
}
|
||||
// matches in regex
|
||||
@@ -142,7 +149,11 @@ class Byte
|
||||
strtolower((string)$number),
|
||||
$matches
|
||||
);
|
||||
if (isset($matches[2]) && isset($matches[3])) {
|
||||
$number_negative = false;
|
||||
if (!empty($matches[1])) {
|
||||
$number_negative = true;
|
||||
}
|
||||
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
|
||||
@@ -152,12 +163,48 @@ class Byte
|
||||
if ($unit) {
|
||||
$number = $number * pow($si ? 1000 : 1024, stripos($valid_units_, $unit[0]) ?: 0);
|
||||
}
|
||||
// if the number is too large, we cannot convert to int directly
|
||||
if ($number <= PHP_INT_MIN || $number >= PHP_INT_MAX) {
|
||||
// if we do not want to convert to string
|
||||
if (!$return_as_string) {
|
||||
throw new \LengthException(
|
||||
'Number too large be converted to int: ' . (string)$number
|
||||
);
|
||||
}
|
||||
// for string, check if bcmath is loaded, if not this will not work
|
||||
if (!extension_loaded('bcmath')) {
|
||||
throw new \RuntimeException(
|
||||
'Number too large be converted to int and BCMath extension not loaded: ' . (string)$number
|
||||
);
|
||||
}
|
||||
}
|
||||
// string return
|
||||
if ($return_as_string) {
|
||||
// return as string to avoid overflow
|
||||
// $number = (string)round($number);
|
||||
$number = bcmul(number_format(
|
||||
$number,
|
||||
12,
|
||||
'.',
|
||||
''
|
||||
), "1");
|
||||
if ($number_negative) {
|
||||
$number = '-' . $number;
|
||||
}
|
||||
return $number;
|
||||
}
|
||||
// convert to INT to avoid +E output
|
||||
$number = (int)round($number);
|
||||
// if negative input, keep nnegative
|
||||
if (!empty($matches[1])) {
|
||||
if ($number_negative) {
|
||||
$number *= -1;
|
||||
}
|
||||
// check if number is negative but should be, this is Lenght overflow
|
||||
if (!$number_negative && $number < 0) {
|
||||
throw new \LengthException(
|
||||
'Number too large be converted to int: ' . (string)$number
|
||||
);
|
||||
}
|
||||
}
|
||||
// if not matching return as is
|
||||
return $number;
|
||||
|
||||
@@ -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)
|
||||
|
||||
+27
-4
@@ -27,10 +27,14 @@ class Json
|
||||
* set original value as array
|
||||
* @return array<mixed> returns an array from the json values
|
||||
*/
|
||||
public static function jsonConvertToArray(?string $json, bool $override = false): array
|
||||
public static function jsonConvertToArray(?string $json, bool $override = false, int $flags = 0): array
|
||||
{
|
||||
if ($json !== null) {
|
||||
$_json = json_decode($json, true);
|
||||
// if flags has JSON_THROW_ON_ERROR remove it
|
||||
if ($flags & JSON_THROW_ON_ERROR) {
|
||||
$flags = $flags & ~JSON_THROW_ON_ERROR;
|
||||
}
|
||||
$_json = json_decode($json, true, flags:$flags);
|
||||
if (self::$json_last_error = json_last_error()) {
|
||||
if ($override == true) {
|
||||
// init return as array with original as element
|
||||
@@ -55,16 +59,35 @@ class Json
|
||||
* Deos not throw errors
|
||||
*
|
||||
* @param array<mixed> $data
|
||||
* @param int $flags json_encode flags as is
|
||||
* @param int $flags [JSON_UNESCAPED_UNICODE] json_encode flags as is
|
||||
* @return string JSON string or '{}' if false
|
||||
*/
|
||||
public static function jsonConvertArrayTo(array $data, int $flags = 0): string
|
||||
public static function jsonConvertArrayTo(array $data, int $flags = JSON_UNESCAPED_UNICODE): string
|
||||
{
|
||||
$json_string = json_encode($data, $flags) ?: '{}';
|
||||
self::$json_last_error = json_last_error();
|
||||
return (string)$json_string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if a json string could be decoded.
|
||||
* 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
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns human readable string for json errors thrown in jsonConvertToArray
|
||||
* Source: https://www.php.net/manual/en/function.json-last-error.php
|
||||
|
||||
+11
-4
@@ -62,10 +62,15 @@ class Math
|
||||
*
|
||||
* @param float $number Number to cubic root
|
||||
* @return float Calculated value
|
||||
* @throws \InvalidArgumentException if $number is negative
|
||||
*/
|
||||
public static function cbrt(float|int $number): float
|
||||
{
|
||||
return pow((float)$number, 1.0 / 3);
|
||||
$value = pow((float)$number, 1.0 / 3);
|
||||
if (is_nan($value)) {
|
||||
throw new \InvalidArgumentException('cube root from this number is not supported: ' . $number);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -199,15 +204,17 @@ class Math
|
||||
callback: fn ($col) => is_array($row) ?
|
||||
array_reduce(
|
||||
array: $row,
|
||||
callback: fn ($a, $v, $i = null) => $a + $v * (
|
||||
// TODO check that v is not an array
|
||||
callback: fn ($a, $v, $i = null) => $a + $v * ( /** @phpstan-ignore-line Possible array + int */
|
||||
// if last entry missing for full copy add a 0 to it
|
||||
$col[$i ?? array_search($v, $row, true)] ?? 0 /** @phpstan-ignore-line */
|
||||
$col[$i ?? array_search($v, $row, true)] ?? 0
|
||||
),
|
||||
initial: 0,
|
||||
) :
|
||||
array_reduce(
|
||||
array: $col,
|
||||
callback: fn ($a, $v) => $a + $v * $row,
|
||||
// TODO check that v is not an array
|
||||
callback: fn ($a, $v) => $a + $v * $row, /** @phpstan-ignore-line Possible array + int */
|
||||
initial: 0,
|
||||
),
|
||||
array: $bCols,
|
||||
|
||||
@@ -268,6 +268,55 @@ class Strings
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Split up character ranges in format A-Z, a-z, 0-9
|
||||
*
|
||||
* @param string $input
|
||||
* @return string[]
|
||||
*/
|
||||
public static function parseCharacterRanges(string $input): array
|
||||
{
|
||||
// if not alphanumeric, throw value error
|
||||
if (!preg_match("/^[A-Za-z0-9\-\s]+$/u", $input)) {
|
||||
throw new \InvalidArgumentException(
|
||||
"The input string contains invalid characters, "
|
||||
. "only alphanumeric, dash (-), space and 'or' are allowed: "
|
||||
. $input
|
||||
);
|
||||
}
|
||||
// Remove all spaces
|
||||
$input = str_replace(' ', '', $input);
|
||||
$result = [];
|
||||
// if there is no - inside, return unique characters as array
|
||||
if (strpos($input, '-') === false) {
|
||||
return array_unique(mb_str_split($input));
|
||||
}
|
||||
// Find all patterns like "A-Z" (character-dash-character)
|
||||
preg_match_all('/(.)-(.)/u', $input, $matches, PREG_SET_ORDER);
|
||||
foreach ($matches as $match) {
|
||||
$start = $match[1];
|
||||
$end = $match[2];
|
||||
// Get ASCII/Unicode values
|
||||
$startOrd = ord($start[0]);
|
||||
$endOrd = ord($end[0]);
|
||||
// make sure start is before end
|
||||
if ($startOrd > $endOrd) {
|
||||
[$startOrd, $endOrd] = [$endOrd, $startOrd];
|
||||
}
|
||||
|
||||
// Generate range of characters
|
||||
for ($i = $startOrd; $i <= $endOrd; $i++) {
|
||||
$char = chr($i);
|
||||
if (!in_array($char, $result)) {
|
||||
$result[] = $char;
|
||||
}
|
||||
}
|
||||
}
|
||||
// make the result unique
|
||||
$result = array_unique($result);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a regex is valid. Does not return the detail regex parser error
|
||||
*
|
||||
|
||||
@@ -27,7 +27,7 @@ class RandomKey
|
||||
/** @var int character count in they key character range */
|
||||
private static int $key_character_range_length = 0;
|
||||
/** @var int default key lenghth */
|
||||
/** @deprecated Will be removed */
|
||||
/** @deprecated Will be removed, as setting has moved to randomKeyGen */
|
||||
private static int $key_length = 4;
|
||||
|
||||
/**
|
||||
@@ -49,7 +49,7 @@ class RandomKey
|
||||
private static function validateRandomKeyData(array ...$key_range): string
|
||||
{
|
||||
$key_character_range = Strings::buildCharStringFromLists(...$key_range);
|
||||
if (strlen(self::$key_character_range) <= 1) {
|
||||
if (strlen($key_character_range) <= 1) {
|
||||
return '';
|
||||
}
|
||||
return $key_character_range;
|
||||
@@ -107,7 +107,7 @@ class RandomKey
|
||||
* @param int $key_length key length
|
||||
* @return bool true for valid, false for invalid length
|
||||
*/
|
||||
private static function validateRandomKeyLenght(int $key_length): bool
|
||||
private static function validateRandomKeyLength(int $key_length): bool
|
||||
{
|
||||
if (
|
||||
$key_length > 0 &&
|
||||
@@ -125,12 +125,12 @@ class RandomKey
|
||||
*
|
||||
* @param int $key_length key length
|
||||
* @return bool true/false for set status
|
||||
* @deprecated This function does no longer set the key length, the randomKeyGen parameter has to b used
|
||||
* @deprecated This function does no longer set the key length, the randomKeyGen parameter has to be used
|
||||
*/
|
||||
public static function setRandomKeyLength(int $key_length): bool
|
||||
{
|
||||
// only if valid int key with valid length
|
||||
if (self::validateRandomKeyLenght($key_length) === true) {
|
||||
if (self::validateRandomKeyLength($key_length) === true) {
|
||||
self::$key_length = $key_length;
|
||||
return true;
|
||||
} else {
|
||||
@@ -176,7 +176,7 @@ class RandomKey
|
||||
$key_character_range_length = self::getRandomKeyDataLength();
|
||||
}
|
||||
// if not valid key length, fallback to default
|
||||
if (!self::validateRandomKeyLenght($key_length)) {
|
||||
if (!self::validateRandomKeyLength($key_length)) {
|
||||
$key_length = self::KEY_LENGTH_DEFAULT;
|
||||
}
|
||||
// create random string
|
||||
|
||||
+1
-1
@@ -81,7 +81,7 @@ class Uids
|
||||
*/
|
||||
public static function validateUuuidv4(string $uuidv4): bool
|
||||
{
|
||||
if (!preg_match("/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/", $uuidv4)) {
|
||||
if (!preg_match("/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i", $uuidv4)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
+15
-9
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -536,7 +536,7 @@ class PgSQL implements Interface\SqlFunctions
|
||||
$connection_string[] = 'sslmode=' . $db_ssl;
|
||||
}
|
||||
// connect
|
||||
$this->dbh = pg_connect(join(' ', $connection_string));
|
||||
$this->dbh = @pg_connect(join(' ', $connection_string));
|
||||
return $this->dbh;
|
||||
}
|
||||
|
||||
|
||||
@@ -263,11 +263,11 @@ class ConvertPlaceholder
|
||||
}
|
||||
}
|
||||
// add the connectors back (1), and the data sets only if no replacement will be done
|
||||
return $params_lookup[$match] ??
|
||||
return $params_lookup[$match]/* ??
|
||||
throw new \RuntimeException(
|
||||
'Cannot lookup ' . $match . ' in params lookup list',
|
||||
211
|
||||
);
|
||||
)*/;
|
||||
},
|
||||
$converted_placeholders['original']['query']
|
||||
);
|
||||
@@ -327,11 +327,11 @@ class ConvertPlaceholder
|
||||
}
|
||||
}
|
||||
// add the connectors back (1), and the data sets only if no replacement will be done
|
||||
return $params_lookup[$match] ??
|
||||
return $params_lookup[$match]/* ??
|
||||
throw new \RuntimeException(
|
||||
'Cannot lookup ' . $match . ' in params lookup list',
|
||||
231
|
||||
);
|
||||
)*/;
|
||||
},
|
||||
$converted_placeholders['original']['query']
|
||||
);
|
||||
|
||||
@@ -32,11 +32,11 @@ class FileWriter
|
||||
if (!is_writeable($folder)) {
|
||||
return false;
|
||||
}
|
||||
// if last is not / then add
|
||||
if (substr($folder, -1, 1) != DIRECTORY_SEPARATOR) {
|
||||
$folder .= DIRECTORY_SEPARATOR;
|
||||
if (!is_dir($folder)) {
|
||||
return false;
|
||||
}
|
||||
self::$debug_folder = $folder;
|
||||
// if last is not / then add
|
||||
self::$debug_folder = rtrim($folder, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+32
-2
@@ -33,6 +33,36 @@ class Support
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* print ISO type datetime with microseconds and timezone
|
||||
* Y-m-dTH:i:s.uP
|
||||
* if no micro time the ".u" part is omitted
|
||||
*
|
||||
* @param bool $set_micro_time
|
||||
* @return string
|
||||
*/
|
||||
public static function printIsoTime(bool $set_micro_time = true): string
|
||||
{
|
||||
$datetime = new \DateTime();
|
||||
|
||||
// Format the DateTime object to ISO 8601 with microseconds
|
||||
// 'Y-m-d\TH:i:s.uP' is the format string:
|
||||
// Y: Full year (e.g., 2025)
|
||||
// m: Month (01-12)
|
||||
// d: Day of the month (01-31)
|
||||
// T: Literal 'T' to separate date and time (escaped with a backslash)
|
||||
// H: Hour (00-23)
|
||||
// i: Minute (00-59)
|
||||
// s: Second (00-59)
|
||||
// u: Microseconds (e.g., 654321)
|
||||
// P: Difference to Greenwich time (GMT) with colon (e.g., +09:00)
|
||||
if ($set_micro_time) {
|
||||
return $datetime->format('Y-m-d\TH:i:s.uP');
|
||||
} else {
|
||||
return $datetime->format('Y-m-d\TH:i:sP');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* prints a html formatted (pre) data
|
||||
*
|
||||
@@ -104,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(
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -131,6 +131,7 @@ class ErrorMessage
|
||||
// set a jump target
|
||||
$this->setJumpTarget($jump_target['target'] ?? null, $jump_target['info'] ?? null, $level);
|
||||
// write to log for abort/crash
|
||||
$this->log->setErrorMessageCallSetErrorMsg();
|
||||
switch ($level) {
|
||||
case 'notice':
|
||||
$this->log->notice($message ?? $str, array_merge([
|
||||
@@ -210,6 +211,7 @@ class ErrorMessage
|
||||
?bool $log_error = null,
|
||||
?bool $log_warning = null,
|
||||
): void {
|
||||
$this->log->setErrorMessageCallSetMessage();
|
||||
$this->setErrorMsg(
|
||||
$error_id ?? '',
|
||||
$level,
|
||||
@@ -289,7 +291,7 @@ class ErrorMessage
|
||||
*/
|
||||
public function getLastErrorMsg(): array
|
||||
{
|
||||
return $this->error_str[array_key_last($this->error_str)] ?? [
|
||||
return $this->error_str[array_key_last($this->error_str) ?? -1] ?? [
|
||||
'level' => '',
|
||||
'str' => '',
|
||||
'id' => '',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -112,7 +112,7 @@ enum Level: int
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the passed $level is higher or equal to $this
|
||||
* Returns true if the passed $level is included in set level
|
||||
*
|
||||
* @param Level $level
|
||||
* @return bool
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace CoreLibs\Logging\Logger;
|
||||
|
||||
enum MessageLevel: int
|
||||
{
|
||||
case noset = 0;
|
||||
case ok = 100;
|
||||
case success = 150; // special for file uploads
|
||||
case info = 200;
|
||||
|
||||
+102
-24
@@ -29,11 +29,19 @@ use Stringable;
|
||||
class Logging
|
||||
{
|
||||
/** @var int minimum size for a max file size, so we don't set 1 byte, 10kb */
|
||||
public const MIN_LOG_MAX_FILESIZE = 10 * 1024;
|
||||
public const int MIN_LOG_MAX_FILESIZE = 10 * 1024;
|
||||
/** @var string log file extension, not changeable */
|
||||
private const LOG_FILE_NAME_EXT = "log";
|
||||
private const string LOG_FILE_NAME_EXT = "log";
|
||||
/** @var string log file block separator, not changeable */
|
||||
private const LOG_FILE_BLOCK_SEPARATOR = '.';
|
||||
private const string LOG_FILE_BLOCK_SEPARATOR = '.';
|
||||
/** @var int the base stack trace level for the line number */
|
||||
private const int DEFAULT_STACK_TRACE_LEVEL_LINE = 1;
|
||||
|
||||
/** @var array<string,int> */
|
||||
private const array STACK_OVERRIDE_CHECK = [
|
||||
'setErrorMsg' => 2,
|
||||
'setMessage' => 3,
|
||||
];
|
||||
|
||||
// MARK: OPTION array
|
||||
// NOTE: the second party array{} hs some errors
|
||||
@@ -96,6 +104,11 @@ class Logging
|
||||
'type' => 'bool', 'mandatory' => false,
|
||||
'default' => false, 'deprecated' => true, 'use' => 'log_per_date'
|
||||
],
|
||||
// if turned off uses old time format without time zone
|
||||
'log_time_format_iso' => [
|
||||
'type' => 'bool', 'mandatory' => false,
|
||||
'default' => true, 'deprecated' => false
|
||||
]
|
||||
];
|
||||
|
||||
// options
|
||||
@@ -133,6 +146,12 @@ class Logging
|
||||
/** @var string Y-m-d file in file name */
|
||||
private string $log_file_date = '';
|
||||
|
||||
// speical flags for ErrorMessage calls
|
||||
/** @var bool Flag to set if called from ErrorMessage::setErrorMsg */
|
||||
private bool $error_message_call_set_error_msg = false;
|
||||
/** @var bool Flag to set if called from ErrorMessage::setMessage */
|
||||
private bool $error_message_call_set_message = false;
|
||||
|
||||
/**
|
||||
* 1: create a new log file per run (time stamp + unique ID)
|
||||
* 2: add Y-m-d and do automatic daily rotation
|
||||
@@ -622,31 +641,65 @@ class Logging
|
||||
$file_line = '';
|
||||
$caller_class_method = '-';
|
||||
$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
||||
// print "[" . $level->getName() . "] [$message] prepareLog:<br>" . Support::printAr($traces);
|
||||
// file + line: call not this but one before (the one that calls this)
|
||||
// start from this level, if unset fall down until we are at null
|
||||
$start_trace_level = 2;
|
||||
for ($trace_level = $start_trace_level; $trace_level >= 0; $trace_level--) {
|
||||
if (isset($traces[$trace_level])) {
|
||||
$file_line = ($traces[$trace_level]['file'] ?? $traces[$trace_level]['function'])
|
||||
. ':' . ($traces[$trace_level]['line'] ?? '-');
|
||||
// as namespace\class->method
|
||||
$caller_class_method =
|
||||
// get the last call before we are in the Logging class
|
||||
($traces[$trace_level]['class'] ?? '')
|
||||
// connector, if unkown use ==
|
||||
. ($traces[$trace_level]['type'] ?? '')
|
||||
// method/function: prepareLog->(debug|info|...)->[THIS]
|
||||
. $traces[$trace_level]['function'];
|
||||
break;
|
||||
$stack_trace_start_level_line = self::DEFAULT_STACK_TRACE_LEVEL_LINE;
|
||||
// set stack trace level +1 if called from ErrorMessage::setMessage
|
||||
if ($this->error_message_call_set_message) {
|
||||
$stack_trace_start_level_line = 3;
|
||||
} elseif ($this->error_message_call_set_error_msg) {
|
||||
$stack_trace_start_level_line = 2;
|
||||
}
|
||||
// if we have line > default, then check if valid, else reset to default
|
||||
if ($stack_trace_start_level_line > self::DEFAULT_STACK_TRACE_LEVEL_LINE) {
|
||||
// check if function at level is one of the override checks
|
||||
$fn_check = $traces[$stack_trace_start_level_line]['function'] ?? '';
|
||||
if (
|
||||
!isset(self::STACK_OVERRIDE_CHECK[$fn_check]) ||
|
||||
self::STACK_OVERRIDE_CHECK[$fn_check] != $stack_trace_start_level_line
|
||||
) {
|
||||
$stack_trace_start_level_line = self::DEFAULT_STACK_TRACE_LEVEL_LINE;
|
||||
}
|
||||
}
|
||||
$this->error_message_call_set_message = false;
|
||||
$this->error_message_call_set_error_msg = false;
|
||||
// set stack trace level +1 if called from ErrorMessage::setMessage
|
||||
// print "[" . $level->getName() . "] [$message] [" . $stack_trace_start_level_line . "] "
|
||||
// . "prepareLog:<br>" . Support::printAr($traces);
|
||||
// file + line: call not this but one before (the one that calls this)
|
||||
// start from this level, if unset fall down until we are at null
|
||||
// NOTE this has to be pushed to 3 for setMessage wrap calls
|
||||
for ($trace_level = $stack_trace_start_level_line; $trace_level >= 0; $trace_level--) {
|
||||
if (!isset($traces[$trace_level])) {
|
||||
continue;
|
||||
}
|
||||
$file_line = ($traces[$trace_level]['file'] ?? $traces[$trace_level]['function'])
|
||||
. ':' . ($traces[$trace_level]['line'] ?? '-');
|
||||
// call function is one stack level above
|
||||
$trace_level++;
|
||||
// skip setting if we are in the top level already
|
||||
if (!isset($traces[$trace_level])) {
|
||||
break;
|
||||
}
|
||||
// as namespace\class->method
|
||||
$caller_class_method =
|
||||
// get the last call before we are in the Logging class
|
||||
($traces[$trace_level]['class'] ?? '')
|
||||
// connector, if unkown use ==
|
||||
. ($traces[$trace_level]['type'] ?? '')
|
||||
// method/function: prepareLog->(debug|info|...)->[THIS]
|
||||
. $traces[$trace_level]['function'];
|
||||
break;
|
||||
}
|
||||
// if not line is set
|
||||
if (empty($file_line)) {
|
||||
$file_line = System::getPageName(System::FULL_PATH);
|
||||
}
|
||||
// print "CLASS: " . $class . "<br>";
|
||||
// get timestamp
|
||||
$timestamp = Support::printTime();
|
||||
if (!empty($this->options['log_time_format_iso'])) {
|
||||
$timestamp = Support::printIsoTime();
|
||||
} else {
|
||||
$timestamp = Support::printTime();
|
||||
}
|
||||
|
||||
// if group id is empty replace it with current level
|
||||
$group_str = $level->getName();
|
||||
@@ -1008,6 +1061,30 @@ class Logging
|
||||
return $this->log_max_filesize;
|
||||
}
|
||||
|
||||
// *********************************************************************
|
||||
// MARK: ErrorMessage class overrides
|
||||
// *********************************************************************
|
||||
|
||||
/**
|
||||
* call if called from Error Message setMessage wrapper
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setErrorMessageCallSetMessage(): void
|
||||
{
|
||||
$this->error_message_call_set_message = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* call if called from Error Message setMessage wrapper
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setErrorMessageCallSetErrorMsg(): void
|
||||
{
|
||||
$this->error_message_call_set_error_msg = true;
|
||||
}
|
||||
|
||||
// *********************************************************************
|
||||
// MARK: OPTIONS CALLS
|
||||
// *********************************************************************
|
||||
@@ -1475,14 +1552,15 @@ class Logging
|
||||
Level::Error, Level::Critical, Level::Alert, Level::Emergency
|
||||
] as $l
|
||||
) {
|
||||
print "Check: " . $this->log_level->getName() . " | " . $l->getName() . "<br>";
|
||||
if ($this->log_level->isHigherThan($l)) {
|
||||
print "L: " . $this->log_level->getName() . " > " . $l->getName() . "<br>";
|
||||
print "L(gt): " . $this->log_level->getName() . " > " . $l->getName() . "<br>";
|
||||
}
|
||||
if ($this->log_level->includes($l)) {
|
||||
print "L: " . $this->log_level->getName() . " <= " . $l->getName() . "<br>";
|
||||
print "L(le): " . $this->log_level->getName() . " <= " . $l->getName() . "<br>";
|
||||
}
|
||||
if ($this->log_level->isLowerThan($l)) {
|
||||
print "L: " . $this->log_level->getName() . " < " . $l->getName() . "<br>";
|
||||
print "L(lt): " . $this->log_level->getName() . " < " . $l->getName() . "<br>";
|
||||
}
|
||||
echo "<br>";
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -365,9 +365,6 @@ class Image
|
||||
imagepng($thumb, $thumbnail_write_path . $thumbnail);
|
||||
break;
|
||||
}
|
||||
// free up resources (in case we are called in a loop)
|
||||
imagedestroy($source);
|
||||
imagedestroy($thumb);
|
||||
} else {
|
||||
throw new \RuntimeException(
|
||||
'Invalid source image file. Only JPEG/PNG are allowed: ' . $filename,
|
||||
@@ -543,8 +540,6 @@ class Image
|
||||
imagepng($img, $filename);
|
||||
break;
|
||||
}
|
||||
// clean up image if we have an image
|
||||
imagedestroy($img);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-21
@@ -614,8 +614,6 @@ class Curl implements Interface\RequestsInterface
|
||||
// print "CURLINFO_HEADER_OUT: <pre>" . curl_getinfo($handle, CURLINFO_HEADER_OUT) . "</pre>";
|
||||
// get response code and bail on not authorized
|
||||
$http_response = $this->handleCurlResponse($handle, $http_result, $options['http_errors']);
|
||||
// close handler
|
||||
$this->handleCurlClose($handle);
|
||||
// return response and result
|
||||
return [
|
||||
'code' => (string)$http_response,
|
||||
@@ -838,17 +836,6 @@ class Curl implements Interface\RequestsInterface
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* close the current curl handle
|
||||
*
|
||||
* @param \CurlHandle $handle
|
||||
* @return void
|
||||
*/
|
||||
private function handleCurlClose(\CurlHandle $handle): void
|
||||
{
|
||||
curl_close($handle);
|
||||
}
|
||||
|
||||
// *********************************************************************
|
||||
// MARK: PUBLIC METHODS
|
||||
// *********************************************************************
|
||||
@@ -967,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
|
||||
@@ -979,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
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ use PHPUnit\Framework\TestCase;
|
||||
*/
|
||||
final class CoreLibsAAASetupDataTest extends TestCase
|
||||
{
|
||||
public const TIME_ZONE = 'Asia/Tokyo';
|
||||
|
||||
/**
|
||||
* Covers nothing
|
||||
*
|
||||
@@ -34,6 +36,9 @@ final class CoreLibsAAASetupDataTest extends TestCase
|
||||
BASE,
|
||||
'BASE Path set check'
|
||||
);
|
||||
|
||||
// if time zone is not set, errors can happen
|
||||
ini_set('date.timezone', self::TIME_ZONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -80,21 +80,27 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
'log_file_id' => 'CoreLibs-ACL-Login-Test',
|
||||
]);
|
||||
// test database we need to connect do, if not possible this test is skipped
|
||||
self::$db = new \CoreLibs\DB\IO(
|
||||
[
|
||||
'db_name' => $db_name,
|
||||
'db_user' => $db_user,
|
||||
'db_pass' => $db_password,
|
||||
'db_host' => $db_host,
|
||||
'db_port' => 5432,
|
||||
'db_schema' => 'public',
|
||||
'db_type' => 'pgsql',
|
||||
'db_encoding' => '',
|
||||
'db_ssl' => 'allow', // allow, disable, require, prefer
|
||||
'db_debug' => true,
|
||||
],
|
||||
self::$log
|
||||
);
|
||||
try {
|
||||
self::$db = new \CoreLibs\DB\IO(
|
||||
[
|
||||
'db_name' => $db_name,
|
||||
'db_user' => $db_user,
|
||||
'db_pass' => $db_password,
|
||||
'db_host' => $db_host,
|
||||
'db_port' => 5432,
|
||||
'db_schema' => 'public',
|
||||
'db_type' => 'pgsql',
|
||||
'db_encoding' => '',
|
||||
'db_ssl' => 'allow', // allow, disable, require, prefer
|
||||
'db_debug' => true,
|
||||
],
|
||||
self::$log
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
self::markTestSkipped(
|
||||
'Cannot connect to Test DB for ACL\Login test: ' . $e->getMessage()
|
||||
);
|
||||
}
|
||||
// ALWAYS drop DB and RECREATE DB
|
||||
// dropdb -U corelibs_acl_login_test -h localhost corelibs_acl_login_test
|
||||
// createdb -U corelibs_acl_login_test -O corelibs_acl_login_test -E utf8 corelibs_acl_login_test;
|
||||
|
||||
@@ -15,6 +15,12 @@ if [ ! -f "${load_sql}" ]; then
|
||||
echo 1;
|
||||
exit 1;
|
||||
fi;
|
||||
# if we have no dropdb, createdb, psql, exit with 6
|
||||
if ! command -v dropdb &> /dev/null || ! command -v createdb &> /dev/null || ! command -v psql &> /dev/null; then
|
||||
echo 6;
|
||||
exit 6;
|
||||
fi;
|
||||
|
||||
db_user="${2}";
|
||||
db_name="${3}";
|
||||
db_host="${4}";
|
||||
@@ -24,25 +30,25 @@ if [ -z "${db_user}" ] || [ -z "${db_name}" ] || [ -z "${db_host}" ]; then
|
||||
exit 2;
|
||||
fi;
|
||||
# drop database, on error exit with 3
|
||||
dropdb -U ${db_user} -h ${db_host} ${db_name} 2>&1;
|
||||
if [ $? -ne 0 ]; then
|
||||
dropdb -U "${db_user}" -h "${db_host}" "${db_name}" 2>&1;
|
||||
if [ ! $? ]; then
|
||||
echo 3;
|
||||
exit 3;
|
||||
fi;
|
||||
# create database, on error exit with 4
|
||||
createdb -U ${db_user} -O ${db_user} -h ${db_host} -E utf8 ${db_name} 2>&1;
|
||||
if [ $? -ne 0 ]; then
|
||||
createdb -U "${db_user}" -O "${db_user}" -h "${db_host}" -E utf8 "${db_name}" 2>&1;
|
||||
if [ ! $? ]; then
|
||||
echo 4;
|
||||
exit 4;
|
||||
fi;
|
||||
# if error 5 thrown, test with enabled below
|
||||
if [ ! -z "${5}" ]; then
|
||||
psql -U ${db_user} -h ${db_host} -f ${load_sql} ${db_name};
|
||||
if [ -n "${5}" ]; then
|
||||
psql -U "${db_user}" -h "${db_host}" -f "${load_sql}" "${db_name}";
|
||||
else
|
||||
# load data (redirect ALL error to null), on error exit with 5
|
||||
psql -U ${db_user} -h ${db_host} -f ${load_sql} ${db_name} 2>&1 1>/dev/null 2>/dev/null;
|
||||
psql -U "${db_user}" -h "${db_host}" -f "${load_sql}" "${db_name}" 2>&1 1>/dev/null 2>/dev/null;
|
||||
fi;
|
||||
if [ $? -ne 0 ]; then
|
||||
if [ ! $? ]; then
|
||||
echo 5;
|
||||
exit 5;
|
||||
fi;
|
||||
|
||||
@@ -24,12 +24,12 @@ final class CoreLibsCheckEmailTest extends TestCase
|
||||
'get email regex invalid -1, will be 0' => [
|
||||
-1,
|
||||
"^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@"
|
||||
. "[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]{1,})*\.([a-zA-Z]{2,}){1}$"
|
||||
. "(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$"
|
||||
],
|
||||
'get email regex invalid 10, will be 0' => [
|
||||
10,
|
||||
"^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@"
|
||||
. "[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]{1,})*\.([a-zA-Z]{2,}){1}$"
|
||||
. "(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$"
|
||||
],
|
||||
'get email regex valid 1, will be 1' => [
|
||||
1,
|
||||
@@ -157,7 +157,7 @@ final class CoreLibsCheckEmailTest extends TestCase
|
||||
'error' => 0,
|
||||
'message' => 'Invalid email address',
|
||||
'regex' => "^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@"
|
||||
. "[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]{1,})*\.([a-zA-Z]{2,}){1}$"
|
||||
. "(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$"
|
||||
]
|
||||
],
|
||||
'error 1 will return double @ error' => [
|
||||
@@ -181,7 +181,7 @@ final class CoreLibsCheckEmailTest extends TestCase
|
||||
[
|
||||
'error' => 3,
|
||||
'message' => 'Invalid domain part after @ sign',
|
||||
'regex' => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.([a-zA-Z]{2,}){1}$"
|
||||
'regex' => "@(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$"
|
||||
]
|
||||
],
|
||||
'error 4 will be invalid domain' => [
|
||||
@@ -189,7 +189,7 @@ final class CoreLibsCheckEmailTest extends TestCase
|
||||
[
|
||||
'error' => 4,
|
||||
'message' => 'Invalid domain name part',
|
||||
'regex' => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\."
|
||||
'regex' => "@(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\."
|
||||
]
|
||||
],
|
||||
'error 5 will be invalid domain top level only' => [
|
||||
@@ -197,7 +197,7 @@ final class CoreLibsCheckEmailTest extends TestCase
|
||||
[
|
||||
'error' => 5,
|
||||
'message' => 'Wrong domain top level part',
|
||||
'regex' => "\.([a-zA-Z]{2,6}){1}$"
|
||||
'regex' => "\.[a-zA-Z]{2,6}$"
|
||||
]
|
||||
],
|
||||
'error 6 will be domain double dot' => [
|
||||
|
||||
@@ -1266,7 +1266,8 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::arrayGetPrevKey, ::arrayGetNextKey
|
||||
* @covers ::arrayGetPrevKey
|
||||
* @covers ::arrayGetNextKey
|
||||
* @dataProvider providerArrayGetNextPrevKey
|
||||
* @testdox arrayGetNextPrevKey get next/prev key for $search wtih $expected_prev/$expected_next [$_dataName]
|
||||
*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php // phpcs:disable Generic.Files.LineLength
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
@@ -71,15 +71,16 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
|
||||
*
|
||||
* @covers ::dateStringFormat
|
||||
* @dataProvider timestampProvider
|
||||
* @testdox dateStringFormat $input (microtime $flag) will be $expected [$_dataName]
|
||||
* @testdox dateStringFormat $input (mt $flag_show_micro, float mt $flag_micro_as_float) will be $expected [$_dataName]
|
||||
*
|
||||
* @param int|float $input
|
||||
* @param bool $flag
|
||||
* @param bool $flag_show_micro
|
||||
* @param bool $flag_micro_as_float
|
||||
* @param string $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testDateStringFormat(
|
||||
$input,
|
||||
int|float $input,
|
||||
bool $flag_show_micro,
|
||||
bool $flag_micro_as_float,
|
||||
string $expected
|
||||
@@ -490,11 +491,11 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
|
||||
],
|
||||
'micro interval with microtime' => [
|
||||
'18999d 0h 38m 10s 1235ms',
|
||||
1641515890.1235,
|
||||
1641515891.235,
|
||||
],
|
||||
'micro interval with microtime' => [
|
||||
'18999d 0h 38m 10s 1234567890ms',
|
||||
1641515890.1234567,
|
||||
1642750457.89,
|
||||
],
|
||||
'negative interval no microtime' => [
|
||||
'-18999d 0h 38m 10s',
|
||||
@@ -503,23 +504,246 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
|
||||
// short for mini tests
|
||||
'microtime only' => [
|
||||
'0s 1235ms',
|
||||
0.1235,
|
||||
1.235,
|
||||
],
|
||||
'seconds only' => [
|
||||
'30s 1235ms',
|
||||
30.1235,
|
||||
31.235,
|
||||
],
|
||||
'minutes only' => [
|
||||
'1m 30s 1235ms',
|
||||
90.1235,
|
||||
91.235,
|
||||
],
|
||||
'hours only' => [
|
||||
'1h 1m 30s 1235ms',
|
||||
3690.1235,
|
||||
3691.235,
|
||||
],
|
||||
'days only' => [
|
||||
'1d 1h 1m 30s 1235ms',
|
||||
90090.1235,
|
||||
90091.235,
|
||||
],
|
||||
'days only with long name' => [
|
||||
'1day 1hour 1min 30second 1235millisecond',
|
||||
90091.235,
|
||||
],
|
||||
// Test day variations
|
||||
'day singular' => [
|
||||
'5day',
|
||||
432000,
|
||||
],
|
||||
'days plural' => [
|
||||
'3days',
|
||||
259200,
|
||||
],
|
||||
'days with space' => [
|
||||
'2days 5h',
|
||||
190800,
|
||||
],
|
||||
'day without space' => [
|
||||
'1day1h',
|
||||
90000,
|
||||
],
|
||||
// Test hour variations
|
||||
'hour singular' => [
|
||||
'2hour',
|
||||
7200,
|
||||
],
|
||||
'hours plural' => [
|
||||
'4hours',
|
||||
14400,
|
||||
],
|
||||
'hours with space' => [
|
||||
'3hours 30m',
|
||||
12600,
|
||||
],
|
||||
'hour without space' => [
|
||||
'1hour30m',
|
||||
5400,
|
||||
],
|
||||
// Test minute variations
|
||||
'min short' => [
|
||||
'45min',
|
||||
2700,
|
||||
],
|
||||
'minute singular' => [
|
||||
'1minute',
|
||||
60,
|
||||
],
|
||||
'minutes plural' => [
|
||||
'10minutes',
|
||||
600,
|
||||
],
|
||||
'minutes with space' => [
|
||||
'5minutes 20s',
|
||||
320,
|
||||
],
|
||||
'min without space' => [
|
||||
'2min30s',
|
||||
150,
|
||||
],
|
||||
// Test second variations
|
||||
'sec short' => [
|
||||
'30sec',
|
||||
30,
|
||||
],
|
||||
'second singular' => [
|
||||
'1second',
|
||||
1,
|
||||
],
|
||||
'seconds plural' => [
|
||||
'45seconds',
|
||||
45,
|
||||
],
|
||||
'seconds with space' => [
|
||||
'15seconds 500ms',
|
||||
15.5,
|
||||
],
|
||||
'sec without space' => [
|
||||
'10sec250ms',
|
||||
10.25,
|
||||
],
|
||||
// Test millisecond variations
|
||||
'ms short' => [
|
||||
'500ms',
|
||||
0.5,
|
||||
],
|
||||
'millis short' => [
|
||||
'250millis',
|
||||
0.25,
|
||||
],
|
||||
'millisec medium singular' => [
|
||||
'250millisec',
|
||||
0.25,
|
||||
],
|
||||
'millisecs medium plural' => [
|
||||
'250millisecs',
|
||||
0.25,
|
||||
],
|
||||
'misec medium singular' => [
|
||||
'250millisec',
|
||||
0.25,
|
||||
],
|
||||
'msecs medium plural' => [
|
||||
'250millisecs',
|
||||
0.25,
|
||||
],
|
||||
'millisecond long singular' => [
|
||||
'1millisecond',
|
||||
0.001,
|
||||
],
|
||||
'milliseconds long plural' => [
|
||||
'999milliseconds',
|
||||
0.999,
|
||||
],
|
||||
// Test negative values
|
||||
'negative days' => [
|
||||
'-5d',
|
||||
-432000,
|
||||
],
|
||||
'negative hours' => [
|
||||
'-3h',
|
||||
-10800,
|
||||
],
|
||||
'negative minutes' => [
|
||||
'-45m',
|
||||
-2700,
|
||||
],
|
||||
'negative seconds' => [
|
||||
'-30s',
|
||||
-30,
|
||||
],
|
||||
'negative milliseconds' => [
|
||||
'-500ms',
|
||||
-0.5,
|
||||
],
|
||||
'negative complex' => [
|
||||
'-2days 3hours 15minutes 30seconds 250milliseconds',
|
||||
-184530.25,
|
||||
],
|
||||
// Test combined formats
|
||||
'all components short' => [
|
||||
'1d 2h 3m 4s 5ms',
|
||||
93784.005,
|
||||
],
|
||||
'all components long' => [
|
||||
'2days 3hours 4minutes 5seconds 678milliseconds',
|
||||
183845.678,
|
||||
],
|
||||
'mixed short and long' => [
|
||||
'1day 2h 3minutes 4sec 100ms',
|
||||
93784.1,
|
||||
],
|
||||
'no spaces between components' => [
|
||||
'1d2h3m4s5ms',
|
||||
93784.005,
|
||||
],
|
||||
'only days and milliseconds' => [
|
||||
'5d 123ms',
|
||||
432000.123,
|
||||
],
|
||||
'only hours and seconds' => [
|
||||
'2h 45s',
|
||||
7245,
|
||||
],
|
||||
'only minutes and milliseconds' => [
|
||||
'30m 500ms',
|
||||
1800.5,
|
||||
],
|
||||
// Test zero values
|
||||
'zero seconds' => [
|
||||
'0s',
|
||||
0,
|
||||
],
|
||||
'zero with milliseconds' => [
|
||||
'0s 123ms',
|
||||
0.123,
|
||||
],
|
||||
// Test large values
|
||||
'large days' => [
|
||||
'365days',
|
||||
31536000,
|
||||
],
|
||||
'large hours' => [
|
||||
'48hours',
|
||||
172800,
|
||||
],
|
||||
'large minutes' => [
|
||||
'1440minutes',
|
||||
86400,
|
||||
],
|
||||
'large seconds' => [
|
||||
'86400seconds',
|
||||
86400,
|
||||
],
|
||||
// Test edge cases with spaces
|
||||
'extra spaces' => [
|
||||
'1d 2h 3m 4s 5ms',
|
||||
93784.005,
|
||||
],
|
||||
'mixed spaces and no spaces' => [
|
||||
'1d 2h3m 4s5ms',
|
||||
93784.005,
|
||||
],
|
||||
// Test single component each
|
||||
'only days short' => [
|
||||
'7d',
|
||||
604800,
|
||||
],
|
||||
'only hours short' => [
|
||||
'12h',
|
||||
43200,
|
||||
],
|
||||
'only minutes short' => [
|
||||
'90m',
|
||||
5400,
|
||||
],
|
||||
'only seconds short' => [
|
||||
'120s',
|
||||
120,
|
||||
],
|
||||
'only milliseconds short' => [
|
||||
'1500ms',
|
||||
1.5,
|
||||
],
|
||||
'already set' => [
|
||||
1641515890,
|
||||
@@ -529,10 +753,18 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
|
||||
'xyz',
|
||||
'xyz',
|
||||
],
|
||||
'empty data' => [
|
||||
' ',
|
||||
' ',
|
||||
],
|
||||
'out of bound data' => [
|
||||
'99999999999999999999d',
|
||||
8.64E+24
|
||||
],
|
||||
'spaces inbetween' => [
|
||||
' - 9 d 2h 58minutes 35 seconds 123 ms ',
|
||||
-788315.123,
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
@@ -555,6 +787,36 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::stringToTime
|
||||
* @testdox stringToTime invalid input will throw exception if requested
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testStringToTimeException(): void
|
||||
{
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessageMatches("/^Invalid time string format, cannot parse: /");
|
||||
\CoreLibs\Combined\DateTime::stringToTime('1x 2y 3z', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::stringToTime
|
||||
* @testdox stringToTime empty input will throw exception if requested
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testStringToTimeExceptionEmpty(): void
|
||||
{
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessageMatches("/^Invalid time string format, no interval value found: /");
|
||||
\CoreLibs\Combined\DateTime::stringToTime(' ', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
|
||||
@@ -123,47 +123,6 @@ final class CoreLibsConvertByteTest extends TestCase
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function byteStringProvider(): array
|
||||
{
|
||||
return [
|
||||
'negative number' => [
|
||||
0 => '-117.42 MB',
|
||||
1 => -123123794,
|
||||
2 => -117420000,
|
||||
],
|
||||
'megabyte' => [
|
||||
0 => '242.98 MB',
|
||||
1 => 254782996,
|
||||
2 => 242980000
|
||||
],
|
||||
'megabyte si' => [
|
||||
0 => '254.78 MiB',
|
||||
1 => 267156193,
|
||||
2 => 254780000
|
||||
],
|
||||
'petabyte' => [
|
||||
0 => '1 EiB',
|
||||
1 => 1152921504606846976,
|
||||
2 => 1000000000000000000,
|
||||
],
|
||||
'max int' => [
|
||||
0 => '8 EB',
|
||||
1 => -9223372036854775807 - 1,
|
||||
2 => 8000000000000000000,
|
||||
],
|
||||
'exabyte, overflow' => [
|
||||
0 => '867.36EB',
|
||||
1 => 3873816255479021568,
|
||||
2 => 363028535651074048,
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -180,7 +139,7 @@ final class CoreLibsConvertByteTest extends TestCase
|
||||
* @return void
|
||||
*/
|
||||
public function testHumanReadableByteFormat(
|
||||
$input,
|
||||
string|int|float $input,
|
||||
string $expected,
|
||||
string $expected_si,
|
||||
string $expected_no_space,
|
||||
@@ -217,6 +176,73 @@ final class CoreLibsConvertByteTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function byteStringProvider(): array
|
||||
{
|
||||
return [
|
||||
'negative number' => [
|
||||
0 => '-117.42 MB',
|
||||
1 => -123123794,
|
||||
2 => -117420000,
|
||||
3 => "-123123793",
|
||||
4 => "-117420000",
|
||||
5 => null,
|
||||
],
|
||||
'megabyte' => [
|
||||
0 => '242.98 MB',
|
||||
1 => 254782996,
|
||||
2 => 242980000,
|
||||
3 => "254782996",
|
||||
4 => "242980000",
|
||||
5 => null,
|
||||
],
|
||||
'megabyte si' => [
|
||||
0 => '254.78 MiB',
|
||||
1 => 267156193,
|
||||
2 => 254780000,
|
||||
3 => "267156193",
|
||||
4 => "254780000",
|
||||
5 => null,
|
||||
],
|
||||
'petabyte' => [
|
||||
0 => '1 EiB',
|
||||
1 => 1152921504606846976,
|
||||
2 => 1000000000000000000,
|
||||
3 => "1152921504606846976",
|
||||
4 => "1000000000000000000",
|
||||
5 => null,
|
||||
],
|
||||
'max int' => [
|
||||
0 => '8 EB',
|
||||
1 => 0,
|
||||
2 => 0,
|
||||
3 => "9223372036854775808",
|
||||
4 => "8000000000000000000",
|
||||
5 => \LengthException::class,
|
||||
],
|
||||
'exabyte, overflow' => [
|
||||
0 => '867.36EB',
|
||||
1 => 0,
|
||||
2 => 0,
|
||||
3 => "999997996235794808832",
|
||||
4 => "867360000000000000000",
|
||||
5 => \LengthException::class,
|
||||
],
|
||||
'huge exabyte, overflow' => [
|
||||
0 => '1000EB',
|
||||
1 => 0,
|
||||
2 => 0,
|
||||
3 => "1152921504606846976000",
|
||||
4 => "1000000000000000000000",
|
||||
5 => \LengthException::class,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -227,10 +253,22 @@ final class CoreLibsConvertByteTest extends TestCase
|
||||
* @param string|int|float $input
|
||||
* @param string|int|float $expected
|
||||
* @param string|int|float $expected_si
|
||||
* @param string|int|float $expected_string
|
||||
* @param string|int|float $expected_string_si
|
||||
* @param ?string $exception
|
||||
* @return void
|
||||
*/
|
||||
public function testStringByteFormat($input, $expected, $expected_si): void
|
||||
{
|
||||
public function testStringByteFormat(
|
||||
string|int|float $input,
|
||||
string|int|float $expected,
|
||||
string|int|float $expected_si,
|
||||
string|int|float $expected_string,
|
||||
string|int|float $expected_string_si,
|
||||
?string $exception
|
||||
): void {
|
||||
if ($exception !== null) {
|
||||
$this->expectException($exception);
|
||||
}
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Convert\Byte::stringByteFormat($input)
|
||||
@@ -239,6 +277,17 @@ final class CoreLibsConvertByteTest extends TestCase
|
||||
$expected_si,
|
||||
\CoreLibs\Convert\Byte::stringByteFormat($input, \CoreLibs\Convert\Byte::BYTE_FORMAT_SI)
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expected_string,
|
||||
\CoreLibs\Convert\Byte::stringByteFormat($input, \CoreLibs\Convert\Byte::RETURN_AS_STRING)
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expected_string_si,
|
||||
\CoreLibs\Convert\Byte::stringByteFormat(
|
||||
$input,
|
||||
\CoreLibs\Convert\Byte::BYTE_FORMAT_SI | \CoreLibs\Convert\Byte::RETURN_AS_STRING
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -101,7 +101,7 @@ final class CoreLibsConvertColorTest extends TestCase
|
||||
* @covers ::rgbToHwb
|
||||
* @covers ::hsbToRgb
|
||||
* @covers ::hslToRgb
|
||||
* @covers ::hwebToRgb
|
||||
* @covers ::hwbToRgb
|
||||
* @testdox Convert from and to RGB via HSL, HWB, HSB/V
|
||||
*
|
||||
* @return void
|
||||
@@ -298,7 +298,6 @@ final class CoreLibsConvertColorTest extends TestCase
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::returnAsHex()
|
||||
* @testdox Convert from and to RGB via hex
|
||||
*
|
||||
* @return void
|
||||
@@ -342,8 +341,6 @@ final class CoreLibsConvertColorTest extends TestCase
|
||||
/**
|
||||
* linear RGB conversion tests
|
||||
*
|
||||
* @covers ::fromLinear
|
||||
* @covers ::toLinear
|
||||
* @testdox Convert from and to RGB linear conversion check
|
||||
*
|
||||
* @return void
|
||||
@@ -933,7 +930,6 @@ final class CoreLibsConvertColorTest extends TestCase
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::__get
|
||||
* @testdox Exception handling for RGB general calls
|
||||
*
|
||||
* @return void
|
||||
@@ -958,7 +954,6 @@ final class CoreLibsConvertColorTest extends TestCase
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::setFromHex
|
||||
* @testdox Exception handling for RGB setFromHex failues
|
||||
*
|
||||
* @return void
|
||||
@@ -1086,7 +1081,6 @@ final class CoreLibsConvertColorTest extends TestCase
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::__get
|
||||
* @testdox Exception handling for Lab general calls
|
||||
*
|
||||
* @return void
|
||||
@@ -1120,7 +1114,6 @@ final class CoreLibsConvertColorTest extends TestCase
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::__get
|
||||
* @testdox Exception handling for LCH general calls
|
||||
*
|
||||
* @return void
|
||||
@@ -1155,7 +1148,6 @@ final class CoreLibsConvertColorTest extends TestCase
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::__get
|
||||
* @testdox Exception handling for XYZ general calls
|
||||
*
|
||||
* @return void
|
||||
|
||||
@@ -164,6 +164,51 @@ final class CoreLibsConvertJsonTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* test with flags
|
||||
*
|
||||
* @covers ::jsonConvertToArray
|
||||
* @testdox jsonConvertToArray flag test, if flag is used
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testJsonConvertToArrayWithFlags(): void
|
||||
{
|
||||
$input = '{"valid":"json","invalid":"\xB1\x31"}';
|
||||
/* $expected_without_flag = [
|
||||
'valid' => 'json'
|
||||
];
|
||||
$expected_with_flag = [
|
||||
'valid' => 'json',
|
||||
'invalid' => "\xB1\x31"
|
||||
]; */
|
||||
// no idea why in both it throws an erro
|
||||
$expected_without_flag = [];
|
||||
$expected_with_flag = [];
|
||||
$this->assertEquals(
|
||||
$expected_without_flag,
|
||||
\CoreLibs\Convert\Json::jsonConvertToArray($input)
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expected_with_flag,
|
||||
\CoreLibs\Convert\Json::jsonConvertToArray($input, flags:JSON_INVALID_UTF8_IGNORE)
|
||||
);
|
||||
}
|
||||
|
||||
public function testJsonConvertToArrayRemoveThrowFlag(): void
|
||||
{
|
||||
$input = '{"valid":"json","invalid":"\xB1\x31"}';
|
||||
// show NOT throw an exception
|
||||
try {
|
||||
$this->assertEquals(
|
||||
[],
|
||||
\CoreLibs\Convert\Json::jsonConvertToArray($input, flags:JSON_THROW_ON_ERROR)
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
$this->fail('Exception was thrown despite flag removal');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* test json error states
|
||||
*
|
||||
@@ -189,6 +234,81 @@ final class CoreLibsConvertJsonTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* test json error states
|
||||
*
|
||||
* @covers ::jsonValidate
|
||||
* @dataProvider jsonErrorProvider
|
||||
* @testdox jsonValidate $input will be $expected_i/$expected_s [$_dataName]
|
||||
*
|
||||
* @param string|null $input
|
||||
* @param int $expected_i
|
||||
* @param string $expected_s
|
||||
* @return void
|
||||
*/
|
||||
public function testJsonValidateGetLastError(?string $input, int $expected_i, string $expected_s): void
|
||||
{
|
||||
\CoreLibs\Convert\Json::jsonValidate($input);
|
||||
$this->assertEquals(
|
||||
$expected_i,
|
||||
\CoreLibs\Convert\Json::jsonGetLastError()
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expected_s,
|
||||
\CoreLibs\Convert\Json::jsonGetLastError(true)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* test json validation
|
||||
*
|
||||
* @covers ::jsonValidate
|
||||
* @testdox jsonValidate test valid and invalid json
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testJsonValidate(): void
|
||||
{
|
||||
$this->assertTrue(
|
||||
\CoreLibs\Convert\Json::jsonValidate('{"valid": "json"}')
|
||||
);
|
||||
$this->assertFalse(
|
||||
\CoreLibs\Convert\Json::jsonValidate('not valid json')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
|
||||
@@ -122,9 +122,9 @@ final class CoreLibsConvertMathTest extends TestCase
|
||||
public function providerCbrt(): array
|
||||
{
|
||||
return [
|
||||
'cube root of 2' => [2, 1.25992, 5],
|
||||
'cube root of 3' => [3, 1.44225, 5],
|
||||
'cube root of -1' => [-1, 'NAN', 0],
|
||||
'cube root of 2' => [2, 1.25992, 5, null],
|
||||
'cube root of 3' => [3, 1.44225, 5, null],
|
||||
'cube root of -1' => [-1, 'NAN', 0, \InvalidArgumentException::class],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -138,10 +138,14 @@ final class CoreLibsConvertMathTest extends TestCase
|
||||
* @param float|int $number
|
||||
* @param float $expected
|
||||
* @param int $round_to
|
||||
* @param ?string $exception
|
||||
* @return void
|
||||
*/
|
||||
public function testCbrt(float|int $number, float|string $expected, int $round_to): void
|
||||
public function testCbrt(float|int $number, float|string $expected, int $round_to, ?string $exception): void
|
||||
{
|
||||
if ($exception !== null) {
|
||||
$this->expectException($exception);
|
||||
}
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
round(\CoreLibs\Convert\Math::cbrt($number), $round_to)
|
||||
|
||||
@@ -671,7 +671,7 @@ final class CoreLibsConvertStringsTest extends TestCase
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::isValidRegexSimple
|
||||
* @covers ::isValidRegex
|
||||
* @dataProvider isValidRegexSimpleProvider
|
||||
* @testdox isValidRegexSimple make $input unqiue to $expected [$_dataName]
|
||||
*
|
||||
@@ -698,6 +698,89 @@ final class CoreLibsConvertStringsTest extends TestCase
|
||||
'Cannot match last preg error string'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function parseCharacterRangesProvider(): array
|
||||
{
|
||||
return [
|
||||
'simple a-z' => [
|
||||
['a-z'],
|
||||
implode('', range('a', 'z')),
|
||||
null,
|
||||
],
|
||||
'simple A-Z' => [
|
||||
['A-Z'],
|
||||
implode('', range('A', 'Z')),
|
||||
null,
|
||||
],
|
||||
'simple 0-9' => [
|
||||
['0-9'],
|
||||
implode('', range('0', '9')),
|
||||
null,
|
||||
],
|
||||
'mixed ranges' => [
|
||||
['a-c', 'X-Z', '3-5'],
|
||||
'abcXYZ345',
|
||||
null,
|
||||
],
|
||||
'reverse ranges' => [
|
||||
['z-a'],
|
||||
'abcdefghijklmnopqrstuvwxyz',
|
||||
null,
|
||||
],
|
||||
'overlapping ranges' => [
|
||||
['a-f', 'd-j'],
|
||||
'abcdefghij',
|
||||
null,
|
||||
],
|
||||
'mixed valid and overlap ranges' => [
|
||||
['a-f', 'z-a', '0-3'],
|
||||
'abcdefghijklmnopqrstuvwxyz0123',
|
||||
null,
|
||||
],
|
||||
'range without dashes' => [
|
||||
['abcddfff'],
|
||||
'abcdf',
|
||||
null,
|
||||
],
|
||||
'invalid ranges' => [
|
||||
['a-あ', 'A-あ', '0-あ'],
|
||||
'',
|
||||
\InvalidArgumentException::class,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::parseCharacterRanges
|
||||
* @dataProvider parseCharacterRangesProvider
|
||||
* @testdox parseCharacterRanges $input to $expected [$_dataName]
|
||||
*
|
||||
* @param array $input
|
||||
* @param string $expected
|
||||
* @param string|null $expected_exception
|
||||
* @return void
|
||||
*/
|
||||
public function testParseCharacterRanges(
|
||||
array $input,
|
||||
string $expected,
|
||||
?string $expected_exception
|
||||
): void {
|
||||
if ($expected_exception !== null) {
|
||||
$this->expectException($expected_exception);
|
||||
}
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
implode('', \CoreLibs\Convert\Strings::parseCharacterRanges(implode('', $input)))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
|
||||
@@ -87,7 +87,7 @@ final class CoreLibsCreateRandomKeyTest extends TestCase
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::randomKeyGeyn
|
||||
* @covers ::randomKeyGen
|
||||
* @dataProvider randomKeyGenProvider
|
||||
* @testdox randomKeyGen use $input key length $expected [$_dataName]
|
||||
*
|
||||
|
||||
@@ -78,7 +78,6 @@ final class CoreLibsCreateSessionTest extends TestCase
|
||||
/**
|
||||
* Test session start
|
||||
*
|
||||
* @covers ::startSession
|
||||
* @dataProvider sessionProvider
|
||||
* @testdox startSession $input name for $type will be $expected (error: $expected_error) [$_dataName]
|
||||
*
|
||||
|
||||
@@ -121,7 +121,6 @@ final class CoreLibsCreateUidsTest extends TestCase
|
||||
* must match 7e78fe0d-59b8-4637-af7f-e88d221a7d1e
|
||||
*
|
||||
* @covers ::uuidv4
|
||||
* @covers ::validateUuidv4
|
||||
* @testdox uuidv4 check that return is matching regex [$_dataName]
|
||||
*
|
||||
* @return void
|
||||
|
||||
@@ -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
|
||||
@@ -124,10 +123,16 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
'log_file_id' => 'CoreLibs-DB-IO-Test',
|
||||
]);
|
||||
// will be true, default logging is true
|
||||
$db = new \CoreLibs\DB\IO(
|
||||
self::$db_config['valid'],
|
||||
self::$log
|
||||
);
|
||||
try {
|
||||
$db = new \CoreLibs\DB\IO(
|
||||
self::$db_config['valid'],
|
||||
self::$log
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
self::markTestSkipped(
|
||||
'Cannot connect to Test DB for DB\IO test: ' . $e->getMessage()
|
||||
);
|
||||
}
|
||||
if (!$db->dbGetConnectionStatus()) {
|
||||
self::markTestSkipped(
|
||||
'Cannot connect to valid Test DB for DB\IO test.'
|
||||
@@ -544,8 +549,6 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
/**
|
||||
* Test dbSetDbug, dbGetDebug
|
||||
*
|
||||
* @covers ::dbGetDbug
|
||||
* @covers ::dbSetDebug
|
||||
* @testdox Set and Get Debug flag
|
||||
*
|
||||
* @return void
|
||||
@@ -3022,8 +3025,6 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
* @covers ::dbReturnParams
|
||||
* @covers ::dbCacheReset
|
||||
* @covers ::dbGetCursorExt
|
||||
* @covers ::dbCursorPos
|
||||
* @covers ::dbCursorNumRows
|
||||
* @dataProvider dbReturnProvider
|
||||
* @testdox dbReturn Read First $read_first_only only and cache $flag_cache and assoc $flag_assoc with (Warning: $warning/Error: $error) [$_dataName]
|
||||
*
|
||||
|
||||
@@ -13,29 +13,6 @@ use PHPUnit\Framework\TestCase;
|
||||
*/
|
||||
final class CoreLibsDebugFileWriterTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fsetFolderProvider(): array
|
||||
{
|
||||
return [
|
||||
'valid log folder name' => [
|
||||
0 => '/tmp/',
|
||||
1 => true,
|
||||
],
|
||||
'invalid log folder name' => [
|
||||
0 => 'some name',
|
||||
1 => false,
|
||||
],
|
||||
'not writeable log folder name' => [
|
||||
0 => '/opt',
|
||||
1 => false,
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -85,18 +62,59 @@ final class CoreLibsDebugFileWriterTest extends TestCase
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @dataProvider fsetFolderProvider
|
||||
* @testdox fsetFolder $input will match $expected [$_dataName]
|
||||
* @testdox fsetFolder test correct return code
|
||||
*
|
||||
* @param string $input
|
||||
* @param boolean $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testFsetFolder(string $input, bool $expected): void
|
||||
public function testFsetFolder(): void
|
||||
{
|
||||
// check the following
|
||||
// - valid folder, writeable
|
||||
// - valid folder, not writeable
|
||||
// - invalid folder (eg file)
|
||||
// - invalid folder name (eg contains spaces), must match ^[\w\-\/]+
|
||||
|
||||
// if we have no /tmp/ folder, we cannot test this, so skip the test
|
||||
if (!is_dir('/tmp')) {
|
||||
$this->markTestSkipped('No /tmp folder found, cannot test fsetFolder');
|
||||
}
|
||||
// TEST 3:
|
||||
// create a file in /tmp/ to test invalid folder (eg file)
|
||||
$test_file = '/tmp/somefile.txt';
|
||||
if (!is_file($test_file)) {
|
||||
touch($test_file);
|
||||
}
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Debug\FileWriter::fsetFolder($input)
|
||||
false,
|
||||
\CoreLibs\Debug\FileWriter::fsetFolder($test_file)
|
||||
);
|
||||
// TEST 4:
|
||||
// test invalid folder name (eg contains spaces), must match ^[\w\-\/]+
|
||||
$this->assertEquals(
|
||||
false,
|
||||
\CoreLibs\Debug\FileWriter::fsetFolder('some name')
|
||||
);
|
||||
// TEST 1:
|
||||
// create a folder in /tmp/ to test valid folder, writeable
|
||||
$test_folder = '/tmp/somefolder';
|
||||
if (!is_dir($test_folder)) {
|
||||
mkdir($test_folder);
|
||||
}
|
||||
$this->assertEquals(
|
||||
true,
|
||||
\CoreLibs\Debug\FileWriter::fsetFolder($test_folder)
|
||||
);
|
||||
// TEST 2:
|
||||
// create a folder in /tmp/ to test valid folder, not writeable
|
||||
$test_folder = '/tmp/somefolder2';
|
||||
if (!is_dir($test_folder)) {
|
||||
mkdir($test_folder);
|
||||
}
|
||||
// remove all write permissions for the folder
|
||||
chmod($test_folder, 0555);
|
||||
$this->assertEquals(
|
||||
false,
|
||||
\CoreLibs\Debug\FileWriter::fsetFolder($test_folder)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -48,8 +48,8 @@ final class CoreLibsDebugSupportTest extends TestCase
|
||||
* @dataProvider printTimeProvider
|
||||
* @testdox printTime test with $microtime and match to regex [$_dataName]
|
||||
*
|
||||
* @param int|null $mircrotime
|
||||
* @param string $expected
|
||||
* @param ?int $microtime
|
||||
* @param string $regex
|
||||
* @return void
|
||||
*/
|
||||
public function testPrintTime(?int $microtime, string $regex): void
|
||||
@@ -460,10 +460,8 @@ final class CoreLibsDebugSupportTest extends TestCase
|
||||
* Undocumented function
|
||||
*
|
||||
* @cover ::getCallerFileLine
|
||||
* @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
|
||||
@@ -471,15 +469,19 @@ final class CoreLibsDebugSupportTest extends TestCase
|
||||
// regex prefix with path "/../" and then fixed vendor + \d+
|
||||
// or phar start if phiev installed
|
||||
// phar:///home/clemens/.phive/phars/phpunit-9.6.13.phar/phpunit/Framework/TestCase.php
|
||||
// phar:///usr/local/bin/phpunit/phpunit/Framework/TestCase.php
|
||||
$regex = "/^("
|
||||
. "\/.*\/vendor\/phpunit\/phpunit\/src"
|
||||
. "|"
|
||||
. "phar:\/\/\/.*\.phive\/phars\/phpunit-\d+\.\d+\.\d+\.phar\/phpunit"
|
||||
. "|"
|
||||
. "phar:\/\/\/usr\/local\/bin\/phpunit\/phpunit"
|
||||
. ")"
|
||||
. "\/Framework\/TestCase.php:\d+$/";
|
||||
$this->assertMatchesRegularExpression(
|
||||
$regex,
|
||||
Support::getCallerFileLine()
|
||||
Support::getCallerFileLine(),
|
||||
'Failed for: ' . Support::getCallerFileLine()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -522,7 +524,7 @@ final class CoreLibsDebugSupportTest extends TestCase
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$compare,
|
||||
'assert expected 10'
|
||||
'assert expected 10: ' . print_r($compare, true)
|
||||
);
|
||||
break;
|
||||
case 11:
|
||||
@@ -545,27 +547,38 @@ final class CoreLibsDebugSupportTest extends TestCase
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$compare,
|
||||
'assert expected 11'
|
||||
'assert expected 11: ' . print_r($compare, true)
|
||||
);
|
||||
break;
|
||||
case 12:
|
||||
// add two "run" before "runBare"
|
||||
array_splice(
|
||||
$expected,
|
||||
7,
|
||||
0,
|
||||
['run']
|
||||
);
|
||||
array_splice(
|
||||
$expected,
|
||||
0,
|
||||
0,
|
||||
['include']
|
||||
);
|
||||
// if compare starts with "main"
|
||||
if ($compare[0] == 'main') {
|
||||
// add two more run after main
|
||||
array_splice(
|
||||
$expected,
|
||||
1,
|
||||
0,
|
||||
['run', 'run']
|
||||
);
|
||||
} else {
|
||||
// add two "run" before "runBare"
|
||||
array_splice(
|
||||
$expected,
|
||||
7,
|
||||
0,
|
||||
['run']
|
||||
);
|
||||
array_splice(
|
||||
$expected,
|
||||
0,
|
||||
0,
|
||||
['include']
|
||||
);
|
||||
}
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$compare,
|
||||
'assert expected 12'
|
||||
'assert expected 12: ' . print_r($compare, true)
|
||||
);
|
||||
break;
|
||||
default:
|
||||
@@ -589,7 +602,7 @@ final class CoreLibsDebugSupportTest extends TestCase
|
||||
if ($call_stack < 8) {
|
||||
$this->assertFalse(true, 'getCallStack too low: 8');
|
||||
} else {
|
||||
$this->assertTrue(true, 'getCallSteck ok');
|
||||
$this->assertTrue(true, 'getCallStack ok');
|
||||
}
|
||||
// just test top entry
|
||||
$first = array_shift($call_stack);
|
||||
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
test.env
|
||||
@@ -442,7 +442,6 @@ final class CoreLibsLanguageL10nTest extends TestCase
|
||||
* init check and connected change translation
|
||||
*
|
||||
* @covers ::getTranslator
|
||||
* @covers ::l10nReloadMOfile
|
||||
* @dataProvider getTranslatorProvider
|
||||
* @testdox change locale from $locale and domain $domain to locale $locale_new and domain $domain_new [$_dataName]
|
||||
*
|
||||
@@ -639,7 +638,7 @@ final class CoreLibsLanguageL10nTest extends TestCase
|
||||
* plural and plural context
|
||||
*
|
||||
* @covers ::__n
|
||||
* @covers ::__pn
|
||||
* @covers ::__np
|
||||
* @dataProvider ngettextProvider
|
||||
* @testdox plural string test for locale $locale and domain $domain with $context [$_dataName]
|
||||
*
|
||||
@@ -865,6 +864,7 @@ final class CoreLibsLanguageL10nTest extends TestCase
|
||||
string $value,
|
||||
string $expected
|
||||
): void {
|
||||
$old_value = null;
|
||||
switch ($type) {
|
||||
case 'global':
|
||||
$GLOBALS[$var] = $value;
|
||||
@@ -1101,11 +1101,11 @@ final class CoreLibsLanguageL10nTest extends TestCase
|
||||
* fuctions check
|
||||
* TODO: others d/dn/dp/dnp gettext functions
|
||||
*
|
||||
* @covers __setlocale
|
||||
* @covers __bindtextdomain
|
||||
* @covers __bind_textdomain_codeset
|
||||
* @covers __textdomain
|
||||
* @covers __gettext
|
||||
* @covers _setlocale
|
||||
* @covers _bindtextdomain
|
||||
* @covers _bind_textdomain_codeset
|
||||
* @covers _textdomain
|
||||
* @covers _gettext
|
||||
* @covers __
|
||||
* @dataProvider functionsProvider
|
||||
* @testdox check functions with locale $locale and domain $domain [$_dataName]
|
||||
|
||||
@@ -105,11 +105,15 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
|
||||
'log_folder' => self::LOG_FOLDER,
|
||||
'log_level' => Level::Error,
|
||||
]);
|
||||
$errorLogTmpfile = tmpfile();
|
||||
$errorLogLocationBackup = ini_set('error_log', stream_get_meta_data($errorLogTmpfile)['uri']);
|
||||
$em = new \CoreLibs\Logging\ErrorMessage($log);
|
||||
$em->setMessage(
|
||||
$level,
|
||||
$str
|
||||
);
|
||||
// for exceptions if log level is set to catch them
|
||||
$error_log_content = stream_get_contents($errorLogTmpfile);
|
||||
$this->assertEquals(
|
||||
[
|
||||
'level' => $expected,
|
||||
@@ -377,6 +381,8 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
|
||||
?bool $log_warning,
|
||||
string $expected
|
||||
): void {
|
||||
$errorLogTmpfile = tmpfile();
|
||||
$errorLogLocationBackup = ini_set('error_log', stream_get_meta_data($errorLogTmpfile)['uri']);
|
||||
$log = new \CoreLibs\Logging\Logging([
|
||||
'log_file_id' => 'testErrorMessagesLogError',
|
||||
'log_folder' => self::LOG_FOLDER,
|
||||
@@ -392,6 +398,9 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
|
||||
log_error: $log_error,
|
||||
log_warning: $log_warning
|
||||
);
|
||||
ini_set('error_log', $errorLogLocationBackup);
|
||||
// for exceptions if log level is set to catch them
|
||||
$error_log_content = stream_get_contents($errorLogTmpfile);
|
||||
$file_content = '';
|
||||
if (is_file($log->getLogFolder() . $log->getLogFile())) {
|
||||
$file_content = file_get_contents(
|
||||
@@ -447,6 +456,8 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
|
||||
'log_level' => Level::Debug,
|
||||
'log_per_run' => true
|
||||
]);
|
||||
$errorLogTmpfile = tmpfile();
|
||||
$errorLogLocationBackup = ini_set('error_log', stream_get_meta_data($errorLogTmpfile)['uri']);
|
||||
$em = new \CoreLibs\Logging\ErrorMessage($log);
|
||||
$em->setErrorMsg(
|
||||
$id,
|
||||
@@ -456,6 +467,9 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
|
||||
log_error: $log_error,
|
||||
log_warning: $log_warning
|
||||
);
|
||||
ini_set('error_log', $errorLogLocationBackup);
|
||||
// for exceptions if log level is set to catch them
|
||||
$error_log_content = stream_get_contents($errorLogTmpfile);
|
||||
$file_content = '';
|
||||
if (is_file($log->getLogFolder() . $log->getLogFile())) {
|
||||
$file_content = file_get_contents(
|
||||
|
||||
@@ -18,7 +18,7 @@ use CoreLibs\Logging\Logger\Flag;
|
||||
final class CoreLibsLoggingLoggingTest extends TestCase
|
||||
{
|
||||
private const LOG_FOLDER = __DIR__ . DIRECTORY_SEPARATOR . 'log' . DIRECTORY_SEPARATOR;
|
||||
private const REGEX_BASE = "\[[\d\-\s\.:]+\]\s{1}" // date
|
||||
private const REGEX_BASE = "\[[\d\-\s\.:+T]+\]\s{1}" // date, just basic checks
|
||||
. "\[[\w\.]+(:\d+)?\]\s{1}" // host:port
|
||||
. "\[(phar:\/\/)?[\w\-\.\/]+:\d+\]\s{1}" // folder/file [note phar:// is for phpunit]
|
||||
. "\[\w+\]\s{1}" // run id
|
||||
@@ -514,7 +514,6 @@ final class CoreLibsLoggingLoggingTest extends TestCase
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers Logger\Flag
|
||||
* @testdox Logger\Flag enum test
|
||||
*
|
||||
* @return void
|
||||
@@ -828,6 +827,7 @@ final class CoreLibsLoggingLoggingTest extends TestCase
|
||||
// clean all data in folder first
|
||||
array_map('unlink', glob($log->getLogFolder() . $log->getLogFileId() . '*.log'));
|
||||
|
||||
$log_status = false;
|
||||
switch ($level->value) {
|
||||
case 200:
|
||||
$log_status = $log->info($message);
|
||||
@@ -851,7 +851,7 @@ final class CoreLibsLoggingLoggingTest extends TestCase
|
||||
$log_status = $log->emergency($message);
|
||||
break;
|
||||
}
|
||||
$this->assertTrue($log_status, 'log write successful');
|
||||
$this->assertTrue($log_status, 'log write unsuccessful for level: ' . $level->getName());
|
||||
$file_content = file_get_contents(
|
||||
$log->getLogFolder() . $log->getLogFile()
|
||||
) ?: '';
|
||||
|
||||
@@ -334,7 +334,6 @@ final class CoreLibsSecurityAsymmetricAnonymousEncryptionTest extends TestCase
|
||||
/**
|
||||
* test encrypt/decrypt produce correct output
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptDecryptSuccess
|
||||
@@ -370,7 +369,6 @@ final class CoreLibsSecurityAsymmetricAnonymousEncryptionTest extends TestCase
|
||||
/**
|
||||
* test encrypt/decrypt produce correct output
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptDecryptSuccess
|
||||
@@ -397,7 +395,6 @@ final class CoreLibsSecurityAsymmetricAnonymousEncryptionTest extends TestCase
|
||||
/**
|
||||
* test encrypt/decrypt produce correct output
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptDecryptSuccess
|
||||
@@ -424,7 +421,6 @@ final class CoreLibsSecurityAsymmetricAnonymousEncryptionTest extends TestCase
|
||||
/**
|
||||
* test encrypt/decrypt produce correct output
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptDecryptSuccess
|
||||
@@ -469,7 +465,6 @@ final class CoreLibsSecurityAsymmetricAnonymousEncryptionTest extends TestCase
|
||||
/**
|
||||
* Test decryption with wrong key
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptFailed
|
||||
@@ -496,7 +491,6 @@ final class CoreLibsSecurityAsymmetricAnonymousEncryptionTest extends TestCase
|
||||
/**
|
||||
* Test decryption with wrong key
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptFailed
|
||||
@@ -521,7 +515,6 @@ final class CoreLibsSecurityAsymmetricAnonymousEncryptionTest extends TestCase
|
||||
/**
|
||||
* Test decryption with wrong key
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptFailed
|
||||
|
||||
@@ -104,7 +104,6 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
/**
|
||||
* test encrypt/decrypt produce correct output
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptDecryptSuccess
|
||||
@@ -132,7 +131,6 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
/**
|
||||
* test encrypt/decrypt produce correct output
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptDecryptSuccess
|
||||
@@ -158,7 +156,6 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
/**
|
||||
* test encrypt/decrypt produce correct output
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encryptKey
|
||||
* @covers ::decryptKey
|
||||
* @dataProvider providerEncryptDecryptSuccess
|
||||
@@ -202,7 +199,6 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
/**
|
||||
* Test decryption with wrong key
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptFailed
|
||||
@@ -228,7 +224,6 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
/**
|
||||
* Test decryption with wrong key
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptFailed
|
||||
@@ -252,7 +247,6 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
/**
|
||||
* Test decryption with wrong key
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encryptKey
|
||||
* @covers ::decryptKey
|
||||
* @dataProvider providerEncryptFailed
|
||||
|
||||
@@ -42,7 +42,6 @@ final class CoreLibsTemplateHtmlBuilderElementTest extends TestCase
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::Element
|
||||
* @covers ::buildHtml
|
||||
* @covers ::getTag
|
||||
* @covers ::getId
|
||||
|
||||
@@ -58,9 +58,12 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
|
||||
if ($handle === false) {
|
||||
continue;
|
||||
}
|
||||
curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
|
||||
$status = curl_exec($handle);
|
||||
if ($status === false) {
|
||||
continue;
|
||||
}
|
||||
$this->url_basic = $url;
|
||||
// split out the last / part for url set test
|
||||
curl_close($handle);
|
||||
// print "Open: $url\n";
|
||||
break;
|
||||
}
|
||||
@@ -461,20 +464,19 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
|
||||
/**
|
||||
* set setup + header, base uri change
|
||||
*
|
||||
* @covers ::Curl
|
||||
* @covers ::setBaseUri
|
||||
* @covers ::addHeader
|
||||
* @covers ::removeHEader
|
||||
* @covers ::setHeaders
|
||||
* @covers ::removeHeaders
|
||||
* @dataProvider providerUrlRequestsCurlSetup
|
||||
* @testdox UrlRequests\Curl Class setup tasks [$_dataName]
|
||||
*
|
||||
* @param null|array $config
|
||||
* @param array $expected
|
||||
* @param null|string $new_base_uri
|
||||
* @param null|array $set_header
|
||||
* @param null|bool $set_header_add
|
||||
* @param null|array $remove_header
|
||||
* @param null|array $expected_change
|
||||
* @param null|array $config
|
||||
* @param array<string,mixed> $expected_set
|
||||
* @param null|string $new_base_uri
|
||||
* @param null|array $set_header
|
||||
* @param null|bool $set_header_add
|
||||
* @param null|array $remove_header
|
||||
* @param null|array $expected_change
|
||||
* @return void
|
||||
*/
|
||||
public function testUrlRequestsCurlSetupConfig(
|
||||
@@ -494,10 +496,18 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
|
||||
};
|
||||
// if ($new_base_uri === null && $set_header === null && $remove_header === null) {
|
||||
// }
|
||||
$this->assertEquals($expected_set, $curl->getConfig(), 'Class setup config mismatch');
|
||||
$this->assertEquals(
|
||||
$expected_set,
|
||||
$curl->getConfig(),
|
||||
'Class setup config mismatch'
|
||||
);
|
||||
if ($new_base_uri !== null) {
|
||||
$curl->setBaseUri($new_base_uri);
|
||||
$this->assertEquals($expected_change, $curl->getConfig(), 'new base_uri not matching');
|
||||
$this->assertEquals(
|
||||
$expected_change,
|
||||
$curl->getConfig(),
|
||||
'new base_uri not matching'
|
||||
);
|
||||
}
|
||||
if ($set_header !== null) {
|
||||
if ($set_header_add !== null) {
|
||||
@@ -505,11 +515,19 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
|
||||
} else {
|
||||
$curl->setHeaders($set_header);
|
||||
}
|
||||
$this->assertEquals($expected_change, $curl->getConfig(), 'new headers not matching');
|
||||
$this->assertEquals(
|
||||
$expected_change,
|
||||
$curl->getConfig(),
|
||||
'new headers not matching'
|
||||
);
|
||||
}
|
||||
if ($remove_header !== null) {
|
||||
$curl->removeHeaders($remove_header);
|
||||
$this->assertEquals($expected_change, $curl->getConfig(), 'removed headers not matching');
|
||||
$this->assertEquals(
|
||||
$expected_change,
|
||||
$curl->getConfig(),
|
||||
'removed headers not matching'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -879,7 +897,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
|
||||
string $return_content
|
||||
) {
|
||||
if (!$this->url_basic) {
|
||||
$this->markTestSkipped('No backend interface setup for testing: GET');
|
||||
$this->markTestSkipped('No backend interface setup for testing');
|
||||
}
|
||||
$curl = new \CoreLibs\UrlRequests\Curl();
|
||||
// options
|
||||
@@ -962,6 +980,9 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
|
||||
*/
|
||||
public function testUrlRequestsCurlRequestMultiple()
|
||||
{
|
||||
if (!$this->url_basic) {
|
||||
$this->markTestSkipped('No backend interface setup for testing');
|
||||
}
|
||||
$curl = new \CoreLibs\UrlRequests\Curl();
|
||||
// get
|
||||
$response = $curl->get($this->url_basic, [
|
||||
@@ -969,76 +990,77 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
|
||||
"query" => ["foo-get" => "bar"]
|
||||
]);
|
||||
$this->assertEquals("200", $response["code"], "multi call: get response code not matching");
|
||||
if (PHP_VERSION_ID >= 80400) {
|
||||
$this->assertEquals(
|
||||
'{"HEADERS":{"HTTP_HOST":"soba.egplusww.jp",'
|
||||
. '"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1","HTTP_FIRST_CALL":"get",'
|
||||
. '"HTTP_ACCEPT":"*\/*"},"REQUEST_TYPE":"GET","PARAMS":{"foo-get":"bar"},"BODY":null}',
|
||||
$response['content'],
|
||||
'multi call: get content not matching'
|
||||
);
|
||||
} else {
|
||||
$this->assertEquals(
|
||||
'{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
||||
. '"HTTP_FIRST_CALL":"get","HTTP_ACCEPT":"*\/*",'
|
||||
. '"HTTP_HOST":"soba.egplusww.jp"},'
|
||||
. '"REQUEST_TYPE":"GET",'
|
||||
. '"PARAMS":{"foo-get":"bar"},"BODY":null}',
|
||||
$response['content'],
|
||||
'multi call: get content not matching'
|
||||
);
|
||||
}
|
||||
$request_expected = json_decode(
|
||||
<<<JSON
|
||||
{
|
||||
"HEADERS":{
|
||||
"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",
|
||||
"HTTP_FIRST_CALL":"get","HTTP_ACCEPT":"*\/*",
|
||||
"HTTP_HOST":"soba.egplusww.jp"
|
||||
},
|
||||
"REQUEST_TYPE":"GET",
|
||||
"PARAMS":{"foo-get":"bar"},"BODY":null
|
||||
}
|
||||
JSON,
|
||||
true
|
||||
);
|
||||
$this->assertEquals(
|
||||
$request_expected,
|
||||
json_decode($response['content'], true),
|
||||
'multi call: get content not matching'
|
||||
);
|
||||
// post
|
||||
$response = $curl->post($this->url_basic, [
|
||||
"headers" => ["second-call" => "post"],
|
||||
"body" => ["foo-post" => "baz"]
|
||||
]);
|
||||
$this->assertEquals("200", $response["code"], "multi call: post response code not matching");
|
||||
if (PHP_VERSION_ID >= 80400) {
|
||||
$this->assertEquals(
|
||||
'{"HEADERS":{"HTTP_HOST":"soba.egplusww.jp",'
|
||||
. '"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
||||
. '"HTTP_SECOND_CALL":"post","HTTP_ACCEPT":"*\/*"},'
|
||||
. '"REQUEST_TYPE":"POST","PARAMS":[],"BODY":{"foo-post":"baz"}}',
|
||||
$response['content'],
|
||||
'multi call: post content not matching'
|
||||
);
|
||||
} else {
|
||||
$this->assertEquals(
|
||||
'{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
||||
. '"HTTP_SECOND_CALL":"post","HTTP_ACCEPT":"*\/*",'
|
||||
. '"HTTP_HOST":"soba.egplusww.jp"},'
|
||||
. '"REQUEST_TYPE":"POST",'
|
||||
. '"PARAMS":[],"BODY":{"foo-post":"baz"}}',
|
||||
$response['content'],
|
||||
'multi call: post content not matching'
|
||||
);
|
||||
}
|
||||
$request_expected = json_decode(
|
||||
<<<JSON
|
||||
{
|
||||
"HEADERS":{
|
||||
"HTTP_HOST":"soba.egplusww.jp",
|
||||
"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",
|
||||
"HTTP_SECOND_CALL":"post",
|
||||
"HTTP_ACCEPT":"*\/*"
|
||||
},
|
||||
"REQUEST_TYPE":"POST",
|
||||
"PARAMS":[],
|
||||
"BODY":{"foo-post":"baz"}
|
||||
}
|
||||
JSON,
|
||||
true
|
||||
);
|
||||
$this->assertEquals(
|
||||
$request_expected,
|
||||
json_decode($response['content'], true),
|
||||
'multi call: post content not matching'
|
||||
);
|
||||
// delete
|
||||
$response = $curl->delete($this->url_basic, [
|
||||
"headers" => ["third-call" => "delete"],
|
||||
]);
|
||||
$this->assertEquals("200", $response["code"], "multi call: delete response code not matching");
|
||||
if (PHP_VERSION_ID >= 80400) {
|
||||
$this->assertEquals(
|
||||
'{"HEADERS":{"HTTP_HOST":"soba.egplusww.jp",'
|
||||
. '"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
||||
. '"HTTP_THIRD_CALL":"delete","HTTP_ACCEPT":"*\/*"},'
|
||||
. '"REQUEST_TYPE":"DELETE","PARAMS":[],"BODY":[]}',
|
||||
$response['content'],
|
||||
'multi call: delete content not matching'
|
||||
);
|
||||
} else {
|
||||
$this->assertEquals(
|
||||
'{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
||||
. '"HTTP_THIRD_CALL":"delete","HTTP_ACCEPT":"*\/*",'
|
||||
. '"HTTP_HOST":"soba.egplusww.jp"},'
|
||||
. '"REQUEST_TYPE":"DELETE",'
|
||||
. '"PARAMS":[],"BODY":[]}',
|
||||
$response['content'],
|
||||
'multi call: delete content not matching'
|
||||
);
|
||||
}
|
||||
$request_expected = json_decode(
|
||||
<<<JSON
|
||||
{
|
||||
"HEADERS":{
|
||||
"HTTP_HOST":"soba.egplusww.jp",
|
||||
"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",
|
||||
"HTTP_THIRD_CALL":"delete","HTTP_ACCEPT":"*\/*"
|
||||
},
|
||||
"REQUEST_TYPE":"DELETE",
|
||||
"PARAMS":[],
|
||||
"BODY":[]
|
||||
}
|
||||
JSON,
|
||||
true
|
||||
);
|
||||
$this->assertEquals(
|
||||
$request_expected,
|
||||
json_decode($response['content'], true),
|
||||
'multi call: delete content not matching'
|
||||
);
|
||||
}
|
||||
|
||||
// MARK: auth header set via config
|
||||
@@ -1052,6 +1074,9 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
|
||||
*/
|
||||
public function testUrlRequestsCurlAuthHeader()
|
||||
{
|
||||
if (!$this->url_basic) {
|
||||
$this->markTestSkipped('No backend interface setup for testing');
|
||||
}
|
||||
$curl = new \CoreLibs\UrlRequests\Curl([
|
||||
"auth" => ["user", "pass", "basic"],
|
||||
"http_errors" => false,
|
||||
@@ -1118,6 +1143,9 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
|
||||
*/
|
||||
public function testExceptionInvalidHeaderKey(): void
|
||||
{
|
||||
if (!$this->url_basic) {
|
||||
$this->markTestSkipped('No backend interface setup for testing');
|
||||
}
|
||||
$curl = new \CoreLibs\UrlRequests\Curl();
|
||||
$this->expectException(\RuntimeException::class);
|
||||
$this->expectExceptionMessageMatches("/InvalidHeaderKey/");
|
||||
@@ -1138,6 +1166,9 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
|
||||
*/
|
||||
public function testExceptionInvalidHeaderValue(): void
|
||||
{
|
||||
if (!$this->url_basic) {
|
||||
$this->markTestSkipped('No backend interface setup for testing');
|
||||
}
|
||||
$curl = new \CoreLibs\UrlRequests\Curl();
|
||||
$this->expectException(\RuntimeException::class);
|
||||
$this->expectExceptionMessageMatches("/InvalidHeaderValue/");
|
||||
@@ -1187,6 +1218,9 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
|
||||
*/
|
||||
public function testExceptionBadRequest(): void
|
||||
{
|
||||
if (!$this->url_basic) {
|
||||
$this->markTestSkipped('No backend interface setup for testing');
|
||||
}
|
||||
$curl = new \CoreLibs\UrlRequests\Curl(["http_errors" => true]);
|
||||
$this->expectException(\RuntimeException::class);
|
||||
$this->expectExceptionMessageMatches("/ClientError/");
|
||||
@@ -1208,6 +1242,9 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
|
||||
*/
|
||||
public function testExceptionBadRequestEnable(): void
|
||||
{
|
||||
if (!$this->url_basic) {
|
||||
$this->markTestSkipped('No backend interface setup for testing');
|
||||
}
|
||||
$curl = new \CoreLibs\UrlRequests\Curl(["http_errors" => false]);
|
||||
$this->expectException(\RuntimeException::class);
|
||||
$this->expectExceptionMessageMatches("/ClientError/");
|
||||
@@ -1230,6 +1267,9 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
|
||||
*/
|
||||
public function testExceptionBadRequestUnset(): void
|
||||
{
|
||||
if (!$this->url_basic) {
|
||||
$this->markTestSkipped('No backend interface setup for testing');
|
||||
}
|
||||
// if true, with false it has to be off
|
||||
$curl = new \CoreLibs\UrlRequests\Curl(["http_errors" => true]);
|
||||
$response = $curl->request('get', $this->url_basic, [
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user