Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
bec16db
fix(codegen): route typed division, int modulo and shifts through PHP…
AlessioGiacobbe Aug 31, 2026
9d520fd
fix(parser): always parenthesize the unary minus operand
AlessioGiacobbe Aug 31, 2026
faa993a
fix(codegen): evaluate compound ??= RHS only when the target is not set
AlessioGiacobbe Aug 31, 2026
82760b4
test(phpunit): anchor TypePhp autoloading to the current checkout
AlessioGiacobbe Aug 31, 2026
b9c01a1
fix(translator): allow overrides to add a by-ref return
AlessioGiacobbe Aug 31, 2026
5548c8a
fix(translator): let a trailing child variadic absorb parent parameters
AlessioGiacobbe Aug 31, 2026
3630c16
fix(translator): validate overrides of built-in class methods
AlessioGiacobbe Aug 31, 2026
b0fcae2
fix(translator): enforce final and abstract parent constructor rules
AlessioGiacobbe Aug 31, 2026
9c74c44
fix(translator): reject static/instance property redeclaration mismatch
AlessioGiacobbe Aug 31, 2026
10228f4
fix(translator): make typed class-constant overrides covariant
AlessioGiacobbe Aug 31, 2026
490f938
test(operator): accept parenthesized negative infinity literal
AlessioGiacobbe Aug 31, 2026
41ca614
fix(parser): defer literal zero divisors to the runtime DivisionByZer…
AlessioGiacobbe Aug 31, 2026
1b2954b
fix(codegen): lower compound assignment and ++/-- on native scalar sl…
AlessioGiacobbe Aug 31, 2026
d462146
fix(translator): validate interface constant contracts
AlessioGiacobbe Aug 31, 2026
07b43cc
fix(gen_stub): spell PHP_INT_MIN constants with ZEND_LONG_MIN
AlessioGiacobbe Aug 31, 2026
578d10d
fix(translator): compare trait data members by value, not source text
AlessioGiacobbe Aug 31, 2026
953c13e
fix(parser): resolve PHP_INT_MAX/MIN folds case-sensitively with name…
AlessioGiacobbe Aug 31, 2026
f33cb85
fix(translator): reject trait adaptations naming nonexistent methods
AlessioGiacobbe Aug 31, 2026
5979247
fix(parser): classify auto-Decimal literals by real mantissa precision
AlessioGiacobbe Aug 31, 2026
dad5293
fix(translator): allow redeclaring a parent's private method
AlessioGiacobbe Aug 31, 2026
d158c20
Merge branch 'audit/oop-validation': OOP validation fixes
AlessioGiacobbe Aug 31, 2026
4e04e37
fix(codegen): register enum-case class constants as real case objects
AlessioGiacobbe Aug 31, 2026
7994da6
fix(preprocessor): enforce Zend enum declaration rules
AlessioGiacobbe Aug 31, 2026
8c28f32
fix(translator): validate abstract method redeclarations against the …
AlessioGiacobbe Aug 31, 2026
29bfa8b
fix(preprocessor): enforce Zend readonly property declaration rules
AlessioGiacobbe Aug 31, 2026
a29f94b
fix(preprocessor): reject abstract method bodies and abstract private…
AlessioGiacobbe Aug 31, 2026
fd1e00d
fix(preprocessor): enforce Zend interface member declaration rules
AlessioGiacobbe Aug 31, 2026
251513d
fix(translator): enforce readonly-class inheritance and interface par…
AlessioGiacobbe Aug 31, 2026
2e185ab
fix(preprocessor): enforce property-hook placement rules for class pr…
AlessioGiacobbe Aug 31, 2026
6ab4658
fix(preprocessor): reject variadic promoted properties and callable p…
AlessioGiacobbe Aug 31, 2026
b2b66d5
test(native-property): assert PHP-semantics lowering for int property +=
AlessioGiacobbe Aug 31, 2026
9d3f7a7
fix(codegen): keep Zend operand read order around hoisted side effects
AlessioGiacobbe Aug 31, 2026
1409e8d
fix(preprocessor): validate compound type declarations and class-scop…
AlessioGiacobbe Aug 31, 2026
0eefba1
Merge branch 'audit/codegen': expression codegen and constant-folding…
AlessioGiacobbe Aug 31, 2026
7b6b9ec
test(preprocessor): cover Zend declaration-rule enforcement
AlessioGiacobbe Aug 31, 2026
865e820
Merge branch 'audit/preproc-validation': declaration validation fixes
AlessioGiacobbe Aug 31, 2026
0e1714f
fix(translator): validate same-name methods when interfaces merge
AlessioGiacobbe Aug 31, 2026
4c7ad4c
Merge upstream master (PR #19 trait composition, gh-38 namespace fix)…
AlessioGiacobbe Aug 31, 2026
7e75c14
fix(ci): platform-neutral codegen assertions, bare negated literals
AlessioGiacobbe Aug 31, 2026
7175652
fix(ci): tolerate forward references in enum case values, align phpt …
AlessioGiacobbe Aug 31, 2026
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
15 changes: 15 additions & 0 deletions phpunit/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@
use TypePhp\Exception\TestError;

require __DIR__ . '/../bin/bootstrap.php';

// The vendor directory may be shared between checkouts (e.g. a git worktree
// with a symlinked vendor/). Composer's autoloader resolves TypePhp\ against
// the checkout that owns vendor/, which would silently test another tree's
// sources. Prepend a loader anchored to THIS checkout so the test suite always
// exercises the code it ships with.
spl_autoload_register(static function (string $class): void {
if (str_starts_with($class, 'TypePhp\\')) {
$path = dirname(__DIR__) . '/src/' . str_replace('\\', '/', substr($class, strlen('TypePhp\\'))) . '.php';
if (is_file($path)) {
require $path;
}
}
}, true, true);

require_once __DIR__ . '/../src/polyfills.php';
require __DIR__ . '/../src/gen_stub.php';

Expand Down
5 changes: 5 additions & 0 deletions phpunit/code/abstract_redeclare_concrete.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php
class A { public function f(): int { return 1; } }
abstract class B extends A { abstract public function f(): int; }

function main() {}
5 changes: 5 additions & 0 deletions phpunit/code/abstract_redeclare_incompatible.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php
abstract class A { abstract public function f(): int; }
abstract class B extends A { abstract public function f(): string; }

function main() {}
4 changes: 4 additions & 0 deletions phpunit/code/abstract_redeclare_internal.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php
abstract class B extends ArrayObject { abstract public function count(): int; }

function main() {}
15 changes: 15 additions & 0 deletions phpunit/code/abstract_redeclare_valid.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php
// Redeclaring an inherited abstract contract compatibly is legal, a parent's
// private method is not inherited, and a trait's abstract requirement may be
// satisfied by an inherited concrete method.
abstract class A { abstract public function f(): int; }
abstract class B extends A { abstract public function f(): int; }

class C { private function g(): int { return 1; } }
abstract class D extends C { abstract public function g(): int; }

trait RequiresF { abstract public function h(): int; }
class E { public function h(): int { return 1; } }
class F extends E { use RequiresF; }

function main() {}
4 changes: 4 additions & 0 deletions phpunit/code/abstract_rule_body.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php
abstract class Job { abstract public function run(): void {} }

function main() {}
4 changes: 4 additions & 0 deletions phpunit/code/abstract_rule_private.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php
abstract class Job { abstract private function run(): void; }

function main() {}
19 changes: 19 additions & 0 deletions phpunit/code/abstract_rule_private_trait_valid.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php
trait JobTrait
{
abstract private function run(): void;

public function go(): void
{
$this->run();
}
}

class Job
{
use JobTrait;

private function run(): void {}
}

function main() {}
21 changes: 21 additions & 0 deletions phpunit/code/coalesce-assign-side-effect-codegen.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

function sideEffectCall(): int
{
echo "side effect!\n";
return 41;
}

function coalesceCompoundRhs(): int
{
$target = 1;
$target ??= sideEffectCall() + 1;
return $target;
}

function coalesceSimpleRhs(): int
{
$target = 1;
$target ??= sideEffectCall();
return $target;
}
17 changes: 17 additions & 0 deletions phpunit/code/const_override_covariant.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php
class A
{
const int|string X = 1;
const mixed M = 1;
const ?int N = null;
}

class B extends A
{
// PHP 8.3 typed constants are covariant: narrowing is allowed.
const int X = 2;
const string M = 'a';
const int N = 5;
}

function main() {}
12 changes: 12 additions & 0 deletions phpunit/code/const_override_unrelated.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php
class A
{
const int X = 1;
}

class B extends A
{
const string X = 'a';
}

function main() {}
12 changes: 12 additions & 0 deletions phpunit/code/const_override_widened.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php
class A
{
const int X = 1;
}

class B extends A
{
const int|string X = 2;
}

function main() {}
4 changes: 4 additions & 0 deletions phpunit/code/const_rule_callable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php
class Bag { const callable FN = 1; }

function main() {}
12 changes: 12 additions & 0 deletions phpunit/code/ctor_override_abstract_incompatible.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php
abstract class A
{
abstract public function __construct(int $x);
}

class B extends A
{
public function __construct(string $x) {}
}

function main() {}
12 changes: 12 additions & 0 deletions phpunit/code/ctor_override_final.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php
class A
{
final public function __construct() {}
}

class B extends A
{
public function __construct() {}
}

function main() {}
12 changes: 12 additions & 0 deletions phpunit/code/ctor_override_final_private.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php
class A
{
final private function __construct() {}
}

class B extends A
{
public function __construct() {}
}

function main() {}
39 changes: 39 additions & 0 deletions phpunit/code/ctor_override_valid.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php
class A
{
public function __construct(int $x) {}
}

// A concrete parent constructor imposes no signature contract: the child may
// change the parameters and even narrow visibility.
class B extends A
{
private function __construct(string $y, array $z)
{
parent::__construct(1);
}
}

class C
{
private function __construct(int $x) {}
}

// A private parent constructor may be redeclared freely.
class D extends C
{
public function __construct(string $y) {}
}

abstract class E
{
abstract public function __construct(int $x);
}

// Adding optional trailing parameters satisfies an abstract constructor.
class F extends E
{
public function __construct(int $x, string $y = '') {}
}

function main() {}
31 changes: 31 additions & 0 deletions phpunit/code/decimal-literal-classification.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

function fifteenSigDigitsWithExponent(): bool
{
return is_float(1.23456789012345e300);
}

function trailingZerosAreNotSignificant(): bool
{
return is_float(999999999999999.0);
}

function roundTripSixteenDigits(): bool
{
return is_float(2.220446049250313E-16);
}

function hexLiteralStaysNumeric(): float
{
return 0x123456789E1234567;
}

function autoDecimalKeepsPromotion()
{
return 3.14159265358979323846;
}

function decimalLiteralDemotesAgainstFloat(float $f): bool
{
return $f == 3.14159265358979323846;
}
23 changes: 23 additions & 0 deletions phpunit/code/enum-case-class-constant.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

enum CaseConstEnum: int
{
case A = 1 + 1;
case B = 4;
}

interface CaseConstInterface
{
const IC = CaseConstEnum::B;
}

class CaseConstHolder implements CaseConstInterface
{
const CB = CaseConstEnum::B;
const CHAIN = self::CB;
}

function main(): void
{
var_dump(CaseConstHolder::CB === CaseConstEnum::B);
}
13 changes: 13 additions & 0 deletions phpunit/code/enum_interface_const_final.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php
interface I
{
final const X = 1;
}

enum E implements I
{
const X = 2;
case A;
}

function main() {}
4 changes: 4 additions & 0 deletions phpunit/code/enum_rule_abstract_method.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php
enum Suit { case Hearts; abstract public function f(): void; }

function main() {}
4 changes: 4 additions & 0 deletions phpunit/code/enum_rule_backing_type.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php
enum Suit: float { case Hearts = 1.0; }

function main() {}
4 changes: 4 additions & 0 deletions phpunit/code/enum_rule_case_const_clash.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php
enum Suit { const Hearts = 1; case Hearts; }

function main() {}
4 changes: 4 additions & 0 deletions phpunit/code/enum_rule_case_missing_value.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php
enum Suit: int { case Hearts; }

function main() {}
4 changes: 4 additions & 0 deletions phpunit/code/enum_rule_case_value_nonbacked.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php
enum Suit { case Hearts = 1; }

function main() {}
4 changes: 4 additions & 0 deletions phpunit/code/enum_rule_duplicate_case.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php
enum Suit { case Hearts; case Hearts; }

function main() {}
5 changes: 5 additions & 0 deletions phpunit/code/enum_rule_extends_enum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php
enum Suit { case Hearts; }
class Deck extends Suit {}

function main() {}
4 changes: 4 additions & 0 deletions phpunit/code/enum_rule_implements_backedenum_nonbacked.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php
enum Suit implements BackedEnum { case Hearts; }

function main() {}
4 changes: 4 additions & 0 deletions phpunit/code/enum_rule_implements_unitenum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php
enum Suit implements UnitEnum { case Hearts; }

function main() {}
4 changes: 4 additions & 0 deletions phpunit/code/enum_rule_magic_construct.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php
enum Suit { case Hearts; public function __construct() {} }

function main() {}
4 changes: 4 additions & 0 deletions phpunit/code/enum_rule_magic_tostring.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php
enum Suit { case Hearts; public function __toString(): string { return ""; } }

function main() {}
4 changes: 4 additions & 0 deletions phpunit/code/enum_rule_property.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php
enum Suit { case Hearts; public int $x = 1; }

function main() {}
4 changes: 4 additions & 0 deletions phpunit/code/enum_rule_static_property.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php
enum Suit { case Hearts; public static int $x = 1; }

function main() {}
4 changes: 4 additions & 0 deletions phpunit/code/enum_rule_valid.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php
enum Suit: string { const Wild = "w"; case Hearts = "h"; case Spades = "s"; public function label(): string { return $this->value; } public function __invoke(): string { return $this->label(); } }

function main() {}
Loading
Loading