Skip to content
Closed
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
106 changes: 106 additions & 0 deletions Zend/tests/instanceof_final.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
--TEST--
instanceof against a final class or an enum
--FILE--
<?php

final class FinalClass {}
class NonFinal {}
class Child extends NonFinal {}

interface Iface {}
final class FinalImpl implements Iface {}

enum UserEnum { case A; }
enum Backed: string { case A = 'a'; }

readonly final class ReadonlyFinal {}

class Base {}
final class Leaf extends Base {
public function test(): void {
var_dump($this instanceof self);
var_dump($this instanceof parent);
var_dump($this instanceof static);
var_dump($this instanceof Leaf);
var_dump($this instanceof Base);
}
}

// Final class: hit and misses.
var_dump(new FinalClass() instanceof FinalClass);
var_dump(new NonFinal() instanceof FinalClass);
var_dump(new Child() instanceof FinalClass);

// A final class implementing an interface still matches the interface.
var_dump(new FinalImpl() instanceof Iface);
var_dump(new NonFinal() instanceof Iface);

// Userland enum: hit and miss against the enum itself.
var_dump(UserEnum::A instanceof UserEnum);
var_dump(new NonFinal() instanceof UserEnum);

// A userland enum implements UnitEnum, a backed enum also implements BackedEnum.
var_dump(UserEnum::A instanceof UnitEnum);
var_dump(UserEnum::A instanceof BackedEnum);
var_dump(Backed::A instanceof UnitEnum);
var_dump(Backed::A instanceof BackedEnum);
var_dump(new NonFinal() instanceof UnitEnum);
var_dump(new NonFinal() instanceof BackedEnum);

// Internal enum (does not carry ZEND_ACC_FINAL, only ZEND_ACC_ENUM).
var_dump(RoundingMode::HalfAwayFromZero instanceof RoundingMode);
var_dump(new NonFinal() instanceof RoundingMode);

// Readonly final class.
var_dump(new ReadonlyFinal() instanceof ReadonlyFinal);
var_dump(new NonFinal() instanceof ReadonlyFinal);

// Non-object left operand.
var_dump(null instanceof FinalClass);
var_dump(1 instanceof FinalClass);
var_dump("x" instanceof FinalClass);
var_dump([] instanceof FinalClass);

// Undefined right operand is not autoloaded and yields false.
var_dump(new NonFinal() instanceof UndefinedClass);

// Right operand as a variable.
$name = FinalClass::class;
var_dump(new FinalClass() instanceof $name);
$name = NonFinal::class;
var_dump(new FinalClass() instanceof $name);

// self, parent and static.
(new Leaf())->test();

?>
--EXPECT--
bool(true)
bool(false)
bool(false)
bool(true)
bool(false)
bool(true)
bool(false)
bool(true)
bool(false)
bool(true)
bool(true)
bool(false)
bool(false)
bool(true)
bool(false)
bool(true)
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
bool(true)
bool(false)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
5 changes: 5 additions & 0 deletions Zend/zend_operators.c
Original file line number Diff line number Diff line change
Expand Up @@ -2648,6 +2648,11 @@ ZEND_API bool ZEND_FASTCALL instanceof_function_slow(const zend_class_entry *ins
}
return 0;
} else {
/* A final class cannot be extended, and an enum is always final, so ce
* cannot be an ancestor of instance_ce. */
if (ce->ce_flags & (ZEND_ACC_FINAL|ZEND_ACC_ENUM)) {
return 0;
}
while (1) {
instance_ce = instance_ce->parent;
if (instance_ce == ce) {
Expand Down
Loading