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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs/GETTING-STARTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ curl -s -b "$jar" 'http://127.0.0.1:8080/example.php' # expect Flash: Saved

**Talking point:** Real session cookies without Zend PHP at dev time — same pattern as production CGI apps. Automated curls: `make examples-sessions-smoke` ([#1887](https://github.com/PurHur/php-compiler/issues/1887)). Gate ladder: `./phpc doctor --gates | grep -i sessions` ([#1903](https://github.com/PurHur/php-compiler/issues/1903)). AOT two-request execute ([#1891](https://github.com/PurHur/php-compiler/issues/1891)) and deploy smoke ([#1893](https://github.com/PurHur/php-compiler/issues/1893)) are opt-in when LLVM is green — see [examples/005-SessionsWeb/README.md](../examples/005-SessionsWeb/README.md) and ROADMAP [#78](https://github.com/PurHur/php-compiler/issues/78).

### 5b. (Optional) ThrowsWeb — caught invalid POST (VM)

`throw` / `catch` on a bad form field ([#2076](https://github.com/PurHur/php-compiler/issues/2076)). Gate ladder: `./phpc doctor --gates | grep -i throws` ([#2102](https://github.com/PurHur/php-compiler/issues/2102)). VM curls: `THROWS_WEB_SMOKE_GATE=1 ./script/examples-web-smoke.sh --throws-only` ([#2093](https://github.com/PurHur/php-compiler/issues/2093)). AOT link/execute gates are opt-in ([#2101](https://github.com/PurHur/php-compiler/issues/2101)) — see [examples/007-ThrowsWeb/README.md](../examples/007-ThrowsWeb/README.md).

### 6. (Optional) Self-host smoke

```bash
Expand Down Expand Up @@ -107,7 +111,7 @@ Needs LLVM + ~8 GiB RAM; includes JIT/AOT lint/link and example smokes.
| `./phpc init --profile sessionsweb dir/` | Scaffold 005-SessionsWeb layout (flat `example.php`) |
| `./phpc init --profile fileupload dir/` | Scaffold 006-FileUploadWeb layout (flat `example.php`) |
| `./phpc doctor` | Environment + optional gate probe |
| `./phpc doctor --gates` | Example web gates + self-host presenter steps ([#1752](https://github.com/PurHur/php-compiler/issues/1752), [#1857](https://github.com/PurHur/php-compiler/issues/1857), [#1871](https://github.com/PurHur/php-compiler/issues/1871)); 005-SessionsWeb ladder: `./phpc doctor --gates \| grep -i sessions` ([#1903](https://github.com/PurHur/php-compiler/issues/1903)) |
| `./phpc doctor --gates` | Example web gates + self-host presenter steps ([#1752](https://github.com/PurHur/php-compiler/issues/1752), [#1857](https://github.com/PurHur/php-compiler/issues/1857), [#1871](https://github.com/PurHur/php-compiler/issues/1871)); 005 ladder: `grep -i sessions` ([#1903](https://github.com/PurHur/php-compiler/issues/1903)); 007 ThrowsWeb: `grep -i throws` ([#2102](https://github.com/PurHur/php-compiler/issues/2102)) |
| `make north-star1-verify` | Example web regression bundle (legacy name; [#1044](https://github.com/PurHur/php-compiler/issues/1044) closed) ([#1845](https://github.com/PurHur/php-compiler/issues/1845)) |
| `make north-star2-verify` | Self-host M0–M4 presenter bundle ([#1865](https://github.com/PurHur/php-compiler/issues/1865); listed in `phpc doctor --gates` when script exists) |
| `make deploy-smoke-all` | Full `PHPC_DEPLOY_ROOT` deploy ladder 001–003 + opt-in 005/006; skip hints when gates `0` — see `./phpc doctor --gates` ([#2077](https://github.com/PurHur/php-compiler/issues/2077)) |
Expand Down
16 changes: 16 additions & 0 deletions docs/local-ci-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,22 @@ make examples-fileupload-deploy-smoke
FILE_UPLOAD_WEB_DEPLOY_SMOKE_GATE=1 ./script/deploy-smoke.sh --example 006
```

## 007-ThrowsWeb gates ([#2076](https://github.com/PurHur/php-compiler/issues/2076), [#2093](https://github.com/PurHur/php-compiler/issues/2093))

Progressive ladder (VM throw/catch → AOT link → AOT execute). VM smoke opt-in ([#2093](https://github.com/PurHur/php-compiler/issues/2093)); AOT gates opt-in until **#195** / **#57** / **#2101** land. Copy-paste ladder: `./phpc doctor --gates` (**#2102**).

| Stage | Variable | Default | When enabled |
|-------|----------|---------|--------------|
| VM throw/catch | `THROWS_WEB_SMOKE_GATE` | `0` | `THROWS_WEB_SMOKE_GATE=1 ./script/examples-web-smoke.sh --throws-only` ([#2093](https://github.com/PurHur/php-compiler/issues/2093)) |
| AOT link | `THROWSWEB_AOT_LINK_GATE` | `0` | `./script/ci-local.sh --filter ThrowsWebAotLinkTest` ([#2101](https://github.com/PurHur/php-compiler/issues/2101)) |
| AOT execute | `THROWSWEB_AOT_SMOKE_GATE` | `0` | `ThrowsWebAotExecuteTest` or `EXAMPLES_AOT_SMOKE_ONLY=007 ./script/examples-aot-smoke.sh` ([#2101](https://github.com/PurHur/php-compiler/issues/2101), [#2104](https://github.com/PurHur/php-compiler/issues/2104)) |

```bash
./phpc doctor --gates | grep -E 'THROWS|007-ThrowsWeb'
THROWS_WEB_SMOKE_GATE=1 ./script/examples-web-smoke.sh --throws-only
THROWSWEB_AOT_LINK_GATE=1 THROWSWEB_AOT_SMOKE_GATE=1 ./script/ci-local.sh --filter ThrowsWebAot
```

**003 AOT execute** (`MINIWEBAPP_AOT_EXECUTE_GATE=1` default; set `0` to skip during iteration):

```bash
Expand Down
82 changes: 82 additions & 0 deletions lib/Doctor.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ public static function runGates(string $repoRoot, bool $noLint = false): int
self::printSelfHostPresenterSection($repoRoot);
self::printSessionsWebSection($repoRoot);
self::printFileUploadWebSection($repoRoot);
self::printThrowsWebSection($repoRoot);

return is_int($exit) ? $exit : 1;
}
Expand Down Expand Up @@ -542,6 +543,87 @@ private static function printFileUploadWebSection(string $repoRoot): void
fwrite(STDOUT, " Docs: examples/006-FileUploadWeb/README.md · docs/local-ci-matrix.md (#2010)\n");
}

/**
* 007-ThrowsWeb gate ladder (issues #2076, #2102).
*/
private static function printThrowsWebSection(string $repoRoot): void
{
$exampleDir = $repoRoot.'/examples/007-ThrowsWeb';
if (!is_dir($exampleDir)) {
return;
}

$defaults = self::readCiDefaultsEnv($repoRoot);
$smokeDefault = $defaults['THROWS_WEB_SMOKE_GATE'] ?? '0';
$linkDefault = $defaults['THROWSWEB_AOT_LINK_GATE'] ?? '0';
$aotDefault = $defaults['THROWSWEB_AOT_SMOKE_GATE'] ?? '0';

$smokeOn = self::gateEnabled('THROWS_WEB_SMOKE_GATE', $smokeDefault);
$linkOn = self::gateEnabled('THROWSWEB_AOT_LINK_GATE', $linkDefault);
$aotOn = self::gateEnabled('THROWSWEB_AOT_SMOKE_GATE', $aotDefault);

$llvmInfo = self::resolveLlvmInfo($repoRoot);
$llvmReady = null !== $llvmInfo['dir'];
$llvmDetail = $llvmReady
? 'LLVM ready at '.$llvmInfo['dir']
: 'LLVM missing — AOT rows need libLLVM-9.so.1';

$hasExample = is_file($exampleDir.'/example.php');
$hasManifest = is_file($exampleDir.'/phpc.json');

fwrite(STDOUT, "\n007-ThrowsWeb CI gates (#2102, ladder #2076–#2101):\n");
fwrite(STDOUT, " Tree: examples/007-ThrowsWeb\n");
fwrite(STDOUT, " {$llvmDetail}\n");
fwrite(STDOUT, " Defaults: script/ci-defaults.env\n\n");

if ($hasExample && $hasManifest) {
fwrite(STDOUT, " [✅] example.php + phpc.json present\n");
} else {
fwrite(STDOUT, " [⬜] example tree incomplete (expected example.php + phpc.json)\n");
}
fwrite(STDOUT, " Lint: ./phpc lint examples/007-ThrowsWeb/example.php\n\n");

self::printSessionsWebGateRow(
1,
'VM throw/catch',
'THROWS_WEB_SMOKE_GATE',
$smokeDefault,
$smokeOn,
false,
'THROWS_WEB_SMOKE_GATE=1 ./script/examples-web-smoke.sh --throws-only · ci-fast when gate=1 (#2093)',
'#2093'
);
self::printSessionsWebGateRow(
2,
'AOT link',
'THROWSWEB_AOT_LINK_GATE',
$linkDefault,
$linkOn,
true,
'./script/ci-local.sh --filter ThrowsWebAotLinkTest (#2101)',
'#2101'
);
$aotStatus = $aotOn && $llvmReady ? '✅' : '📋';
$aotExecuteNote = $llvmReady
? ($aotOn ? '#2101' : '#2101 · opt-in until throw/catch AOT green')
: 'LLVM required; #2101 when gate=1';
fwrite(STDOUT, " [{$aotStatus}] Stage 3 AOT execute — THROWSWEB_AOT_SMOKE_GATE default {$aotDefault} ({$aotExecuteNote})\n");
fwrite(STDOUT, " PHPUnit: ./script/ci-local.sh --filter ThrowsWebAotExecuteTest\n");
fwrite(STDOUT, " Shell: THROWSWEB_AOT_SMOKE_GATE=1 EXAMPLES_AOT_SMOKE_ONLY=007 ./script/examples-aot-smoke.sh (#2104)\n");

$initProfileLive = \PHPCompiler\Cli\PhpcInit::isKnownProfile('throwsweb');
$initTemplate = is_file($repoRoot.'/templates/init-throwsweb/example.php');
fwrite(STDOUT, "\n Related:\n");
if ($initProfileLive) {
fwrite(STDOUT, " [✅] phpc init --profile throwsweb (#2092)\n");
} elseif ($initTemplate) {
fwrite(STDOUT, " [📋] phpc init --profile throwsweb — template ready; CLI profile pending #2092\n");
} else {
fwrite(STDOUT, " [📋] phpc init --profile throwsweb — #2092\n");
}
fwrite(STDOUT, " Docs: examples/007-ThrowsWeb/README.md · docs/local-ci-matrix.md (#2102)\n");
}

/**
* @param array<string, string> $defaults
*/
Expand Down
2 changes: 2 additions & 0 deletions script/ci-defaults.env
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export SESSIONS_WEB_DEPLOY_SMOKE_GATE="${SESSIONS_WEB_DEPLOY_SMOKE_GATE:-0}" #
export FILE_UPLOAD_WEB_DEPLOY_SMOKE_GATE="${FILE_UPLOAD_WEB_DEPLOY_SMOKE_GATE:-0}" # opt-in (#2028); 006 deploy + PHPC_DEPLOY_ROOT multipart upload
export FILE_UPLOAD_WEB_SMOKE_GATE="${FILE_UPLOAD_WEB_SMOKE_GATE:-1}" # default on (#2009); 006 multipart curl via examples-web-smoke
export THROWS_WEB_SMOKE_GATE="${THROWS_WEB_SMOKE_GATE:-0}" # opt-in (#2076, #2093); 007-ThrowsWeb caught invalid POST curls
export THROWSWEB_AOT_LINK_GATE="${THROWSWEB_AOT_LINK_GATE:-0}" # opt-in (#2101); 007 native link when throw/catch AOT green
export THROWSWEB_AOT_SMOKE_GATE="${THROWSWEB_AOT_SMOKE_GATE:-0}" # opt-in (#2101); 007 AOT CGI invalid POST execute
export FILE_UPLOAD_WEB_AOT_LINK_GATE="${FILE_UPLOAD_WEB_AOT_LINK_GATE:-1}" # default on (#2011); set 0 to skip 006 link during iteration
export FILE_UPLOAD_WEB_AOT_SMOKE_GATE="${FILE_UPLOAD_WEB_AOT_SMOKE_GATE:-1}" # default on (#2012); 006 AOT CGI multipart execute
export BOOTSTRAP_SELFHOST_PROBE_UPDATE="${BOOTSTRAP_SELFHOST_PROBE_UPDATE:-0}"
Expand Down
17 changes: 17 additions & 0 deletions test/unit/CiScriptsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1202,6 +1202,23 @@ public function testLocalCiMatrixDocumentsSessionsWebGates(): void
$this->assertStringContainsString('#1969', $doc);
}

public function testLocalCiMatrixDocumentsThrowsWebGates(): void
{
$doc = (string) file_get_contents(dirname(__DIR__, 2).'/docs/local-ci-matrix.md');
$this->assertStringContainsString('## 007-ThrowsWeb gates', $doc);
$this->assertStringContainsString('THROWS_WEB_SMOKE_GATE', $doc);
$this->assertStringContainsString('THROWSWEB_AOT_LINK_GATE', $doc);
$this->assertStringContainsString('THROWSWEB_AOT_SMOKE_GATE', $doc);
$this->assertStringContainsString('#2102', $doc);
}

public function testCiDefaultsEnvDefinesThrowsWebAotGatesOff(): void
{
$defaults = (string) file_get_contents(dirname(__DIR__, 2).'/script/ci-defaults.env');
$this->assertStringContainsString('THROWSWEB_AOT_LINK_GATE="${THROWSWEB_AOT_LINK_GATE:-0}"', $defaults);
$this->assertStringContainsString('THROWSWEB_AOT_SMOKE_GATE="${THROWSWEB_AOT_SMOKE_GATE:-0}"', $defaults);
}

public function testCiFastRunsMiniWebAppVmCliGateByDefault(): void
{
$fast = dirname(__DIR__, 2).'/script/ci-fast.sh';
Expand Down
10 changes: 10 additions & 0 deletions test/unit/PhpcDoctorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,16 @@ public function testDoctorGatesPrintsMiniWebAppLadder(): void
$this->assertStringContainsString('FileUploadWebAotExecuteTest', $result['stdout']);
$this->assertStringContainsString('#2004', $result['stdout']);
$this->assertStringContainsString('#2010', $result['stdout']);
$this->assertStringContainsString('007-ThrowsWeb CI gates', $result['stdout']);
$this->assertStringContainsString('007-ThrowsWeb', $result['stdout']);
$this->assertStringContainsString('THROWS_WEB_SMOKE_GATE', $result['stdout']);
$this->assertStringContainsString('THROWSWEB_AOT_LINK_GATE', $result['stdout']);
$this->assertStringContainsString('THROWSWEB_AOT_SMOKE_GATE', $result['stdout']);
$this->assertStringContainsString('--throws-only', $result['stdout']);
$this->assertStringContainsString('#2102', $result['stdout']);
$this->assertStringContainsString('#2093', $result['stdout']);
$this->assertStringContainsString('#2101', $result['stdout']);
$this->assertStringContainsString('examples/007-ThrowsWeb/README.md', $result['stdout']);
}

/**
Expand Down