Skip to content

Commit 79e9cbc

Browse files
committed
Merge branch 'php8.4' of https://github.com/LongTermSupport/php-qa-ci into php8.4
2 parents 292d551 + b097836 commit 79e9cbc

3 files changed

Lines changed: 93 additions & 17 deletions

File tree

bin/qa

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,19 @@
1111
# Run all PHPUnit tests:
1212
# phpUnitQuickTests=0 ./bin/qa
1313

14+
# Early --json detection: redirect all decoration to stderr before any echo
15+
# Full --json validation happens later in options.inc.bash
16+
_qa_has_json_flag=0
17+
for _qa_arg in "$@"; do
18+
[[ "$_qa_arg" == "--json" ]] && _qa_has_json_flag=1 && break
19+
done
20+
if [[ "$_qa_has_json_flag" == "1" ]]; then
21+
exec 3>&1 1>&2
22+
else
23+
exec 3>&1
24+
fi
25+
unset _qa_has_json_flag _qa_arg
26+
1427
if [[ "$COMPOSER_RUNTIME_BIN_DIR" != "" ]]; then
1528
echo "new composer detected, no longer a symlink, instead its a parent script"
1629
binDir="$COMPOSER_RUNTIME_BIN_DIR"
@@ -49,6 +62,9 @@ $(hostname) $0 $@
4962

5063
source ./../includes/options.inc.bash
5164

65+
# Note: fd 3 redirection is set up at the top of this script (early --json detection)
66+
# so that ALL decoration output goes to stderr before any echo statements.
67+
5268
#### FUNCTIONS #################################################
5369

5470
phpBinPath=${PHP_QA_CI_PHP_EXECUTABLE:-$(which php)}

includes/generic/phpstan.inc.bash

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,57 @@ if [[ "true" == "$CI" ]]; then
99
phpstanNoProgress+=(--no-progress)
1010
fi
1111

12-
while ((phpStanExitCode > 0)); do
13-
# Run PHPStan with tee to capture output to both file and stdout
12+
if [[ "1" == "${useJsonOutput:-0}" ]]; then
13+
# JSON mode: single run, no retry loop, structured output
14+
phpStanJsonFile="$phpStanLogDir/phpstan.json"
15+
1416
phpNoXdebug -f "$pharDir"/phpstan.phar -- \
1517
analyse ${pathsToCheck[@]} \
1618
-c "$phpstanConfigPath" \
17-
${phpstanNoProgress[@]:-} \
18-
2>&1 | tee "$phpStanLogDir/$phpStanLogFile"
19+
--no-progress \
20+
--error-format=json \
21+
> "$phpStanJsonFile"
22+
23+
phpStanExitCode=$?
1924

20-
phpStanExitCode=${PIPESTATUS[0]}
25+
# Output JSON to fd 3 (original stdout, bypassing stderr redirect)
26+
cat "$phpStanJsonFile" >&3
2127

22-
# Archive PHPStan log with timestamp - keep last 10 per pattern
23-
# Do this BEFORE tryAgainOrAbort so log is archived even on failure (in CI mode)
24-
# Uses shared archiveToolLog function from functions.inc.bash
25-
archiveToolLog "PHPStan" "$phpStanLogDir" "$phpStanLogFile" "$specifiedPath" "${pathsToCheck[@]}"
28+
# Archive the JSON log
29+
archiveToolLog "PHPStan" "$phpStanLogDir" "phpstan.json" "$specifiedPath" "${pathsToCheck[@]}"
2630

27-
#exit code 0 = fine, 1 = ran fine but found errors, else it means it crashed
2831
if ((phpStanExitCode > 1)); then
29-
printf "\n\n\nPHPStan Crashed!!....\n\nrunning again with debug mode:\nWhere ever it stops is probably a fatal PHP error\n\n"
30-
eval phpNoXdebug -f "$pharDir"/phpstan.phar -- analyse $pathsStringArray -c "$phpstanConfigPath" --debug -v
32+
echo "PHPStan crashed (exit code: $phpStanExitCode)" >&2
3133
exit 1
3234
fi
33-
if ((phpStanExitCode > 0)); then
34-
tryAgainOrAbort "PHPStan"
35-
fi
36-
done
35+
# Exit code 1 = found errors — expected for JSON consumption, don't retry
36+
else
37+
# Text mode: original behavior with retry loop
38+
while ((phpStanExitCode > 0)); do
39+
# Run PHPStan with tee to capture output to both file and stdout
40+
phpNoXdebug -f "$pharDir"/phpstan.phar -- \
41+
analyse ${pathsToCheck[@]} \
42+
-c "$phpstanConfigPath" \
43+
${phpstanNoProgress[@]:-} \
44+
2>&1 | tee "$phpStanLogDir/$phpStanLogFile"
45+
46+
phpStanExitCode=${PIPESTATUS[0]}
47+
48+
# Archive PHPStan log with timestamp - keep last 10 per pattern
49+
# Do this BEFORE tryAgainOrAbort so log is archived even on failure (in CI mode)
50+
# Uses shared archiveToolLog function from functions.inc.bash
51+
archiveToolLog "PHPStan" "$phpStanLogDir" "$phpStanLogFile" "$specifiedPath" "${pathsToCheck[@]}"
52+
53+
#exit code 0 = fine, 1 = ran fine but found errors, else it means it crashed
54+
if ((phpStanExitCode > 1)); then
55+
printf "\n\n\nPHPStan Crashed!!....\n\nrunning again with debug mode:\nWhere ever it stops is probably a fatal PHP error\n\n"
56+
eval phpNoXdebug -f "$pharDir"/phpstan.phar -- analyse $pathsStringArray -c "$phpstanConfigPath" --debug -v
57+
exit 1
58+
fi
59+
if ((phpStanExitCode > 0)); then
60+
tryAgainOrAbort "PHPStan"
61+
fi
62+
done
63+
fi
3764

3865
set -e

includes/options.inc.bash

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,30 @@ IFS=$'\n\t'
99

1010
singleToolToRun=
1111
specifiedPath=
12+
useJsonOutput=0
13+
14+
# Pre-process long options (getopts only handles short options)
15+
processedArgs=()
16+
for arg in "$@"; do
17+
case "$arg" in
18+
--json) useJsonOutput=1 ;;
19+
*) processedArgs+=("$arg") ;;
20+
esac
21+
done
22+
set -- "${processedArgs[@]}"
1223

1324
function usage {
1425
echo "Usage:"
15-
echo "$binDir/qa [-t tool to run ] [ -p path to scan ]"
26+
echo "$binDir/qa [-t tool to run ] [ -p path to scan ] [ --json ]"
1627
echo ""
1728
echo "Defaults to using all tools and scanning whole project based on platform"
1829
echo ""
1930
echo " - use -h to see this help"
2031
echo ""
2132
echo " - use -p to specify a specific path to scan"
2233
echo ""
34+
echo " - use --json to get structured JSON output (supported tools only)"
35+
echo ""
2336
echo " - use -t to specify a single tool:"
2437
echo " allLints all linting tools"
2538
echo " allStatic all static analysis tools"
@@ -178,6 +191,26 @@ then
178191
echo "Running Single Tool: $singleToolToRun"
179192
fi
180193

194+
# Validate JSON output compatibility
195+
if [[ "1" == "$useJsonOutput" ]]; then
196+
if [[ -z "$singleToolToRun" ]]; then
197+
printf "\nERROR: --json requires a single tool (-t)\n" >&2
198+
printf " Example: vendor/bin/qa -t phpstan --json\n\n" >&2
199+
exit 1
200+
fi
201+
202+
case "$singleToolToRun" in
203+
phpstan) ;; # Native --error-format=json
204+
*)
205+
printf "\nERROR: --json is not yet supported for '%s'\n\n" "$singleToolToRun" >&2
206+
printf "Tools with --json support:\n" >&2
207+
printf " phpstan (native --error-format=json)\n\n" >&2
208+
printf "Run without --json, or use a supported tool.\n\n" >&2
209+
exit 1
210+
;;
211+
esac
212+
fi
213+
181214
if [[ "" != "$specifiedPath" ]]
182215
then
183216
echo "Scanning Specified Path: $specifiedPath"

0 commit comments

Comments
 (0)