parent
c0328df9e6
commit
f56b209d5c
7 changed files with 130 additions and 3 deletions
@ -1,7 +1,8 @@ |
|||||||
# EXT/LIB integration tests |
# EXT/LIB integration tests |
||||||
|
|
||||||
This suite protects build-mode boundaries rather than duplicating the PHP |
This suite lives below `.github` because repository test fixtures with a `.php` |
||||||
syntax coverage in `tests/compiler`. |
suffix are intentionally ignored below `tests/`. It protects build-mode |
||||||
|
boundaries rather than duplicating the PHP syntax coverage in `tests/compiler`. |
||||||
|
|
||||||
- `ext/lifecycle` builds a real Zend extension and loads it through CLI, |
- `ext/lifecycle` builds a real Zend extension and loads it through CLI, |
||||||
`php -S`, and PHP-FPM. The long-running hosts alternate implementations of |
`php -S`, and PHP-FPM. The long-running hosts alternate implementations of |
||||||
@ -0,0 +1,57 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
$request = PHP_SAPI === 'cli' |
||||||
|
? (int) getenv('TYPEPHP_INTEGRATION_REQUEST') |
||||||
|
: (int) ($_GET['request'] ?? 0); |
||||||
|
|
||||||
|
// Alternate the implementation attached to the same request-local symbols. |
||||||
|
// A stale pointer surviving RSHUTDOWN will either call the preceding request's |
||||||
|
// implementation, access released memory, or crash the long-running host. |
||||||
|
if ($request % 2 === 0) { |
||||||
|
final class TypePhpIntegrationRequestValue |
||||||
|
{ |
||||||
|
public function __construct(private int $value) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public function render(): string |
||||||
|
{ |
||||||
|
return 'even:' . $this->value; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function typephp_integration_request_transform(string $value): string |
||||||
|
{ |
||||||
|
return 'even-handler[' . $value . ']'; |
||||||
|
} |
||||||
|
} else { |
||||||
|
final class TypePhpIntegrationRequestValue |
||||||
|
{ |
||||||
|
public function __construct(private int $value) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public function render(): string |
||||||
|
{ |
||||||
|
return 'odd:' . $this->value; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function typephp_integration_request_transform(string $value): string |
||||||
|
{ |
||||||
|
return 'odd-handler[' . $value . ']'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
header('Content-Type: application/json'); |
||||||
|
echo json_encode([ |
||||||
|
'request' => $request, |
||||||
|
'results' => [ |
||||||
|
typephp_integration_probe($request), |
||||||
|
typephp_integration_probe($request), |
||||||
|
], |
||||||
|
'main_registered' => function_exists('main'), |
||||||
|
'pid' => getmypid(), |
||||||
|
], JSON_THROW_ON_ERROR); |
||||||
@ -0,0 +1,24 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
function typephp_integration_probe(int $request): string |
||||||
|
{ |
||||||
|
static $calls = 0; |
||||||
|
++$calls; |
||||||
|
|
||||||
|
// DateTimeImmutable and its format() method are internal, module-lifetime |
||||||
|
// symbols. The host class/function below are rebuilt for every request and |
||||||
|
// therefore exercise the request-lifetime class/function cache domain. |
||||||
|
$date = new DateTimeImmutable('@' . $request); |
||||||
|
$value = new TypePhpIntegrationRequestValue($request); |
||||||
|
return $calls . '@' . $date->format('U') . '|' |
||||||
|
. typephp_integration_request_transform($value->render()); |
||||||
|
} |
||||||
|
|
||||||
|
// main() belongs to the embedded bin entry path. An extension must neither |
||||||
|
// register it as a PHP function nor invoke it from RINIT. |
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
throw new RuntimeException('ext mode invoked bin main()'); |
||||||
|
} |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
use TypePhpIntegration\Library\Counter; |
||||||
|
use function TypePhpIntegration\Library\add; |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
echo add(19, 23), "\n"; |
||||||
|
|
||||||
|
$counter = new Counter(); |
||||||
|
$counter->add(3); |
||||||
|
$counter->add(4); |
||||||
|
echo 'counter=', $counter->value, "\n"; |
||||||
|
} |
||||||
@ -0,0 +1,29 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace { |
||||||
|
// lib mode owns an embedded module but must not execute the bin entrypoint. |
||||||
|
// This declaration must also be omitted from the published import stub. |
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
throw new RuntimeException('lib mode invoked bin main()'); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
namespace TypePhpIntegration\Library { |
||||||
|
function add(int $left, int $right): int |
||||||
|
{ |
||||||
|
return $left + $right; |
||||||
|
} |
||||||
|
|
||||||
|
final class Counter |
||||||
|
{ |
||||||
|
public int $value = 0; |
||||||
|
|
||||||
|
public function add(int $delta): int |
||||||
|
{ |
||||||
|
return $this->value += $delta; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue