fix(translator): 修复trait parent::调用的函数声明参数不一致 #38
Merged
韩天峰
merged 3 commits from translator-fix-trait-parent-funcdecl-params into master 1 month ago
5 changed files with 166 additions and 23 deletions
@ -0,0 +1,42 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
class Base |
||||||
|
{ |
||||||
|
public function __construct(array $option = []) |
||||||
|
{ |
||||||
|
echo 'Base:'; |
||||||
|
foreach ($option as $k => $v) { |
||||||
|
echo ' ' . $k . '=' . $v; |
||||||
|
} |
||||||
|
echo "\n"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
trait TPdoDriver |
||||||
|
{ |
||||||
|
public function __construct(array $option = []) |
||||||
|
{ |
||||||
|
$option['fromTrait'] = 1; |
||||||
|
parent::__construct($option); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class Driver extends Base |
||||||
|
{ |
||||||
|
use TPdoDriver { |
||||||
|
__construct as private tPdoDriverConstruct; |
||||||
|
} |
||||||
|
|
||||||
|
public function __construct(array $option = []) |
||||||
|
{ |
||||||
|
$option['username'] = 'postgres'; |
||||||
|
$this->tPdoDriverConstruct($option); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class DirectDriver extends Base |
||||||
|
{ |
||||||
|
use TPdoDriver; |
||||||
|
} |
||||||
@ -0,0 +1,45 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/** |
||||||
|
* Regression test for the trait `parent::` / `trait_parent_ce` declaration bug. |
||||||
|
* |
||||||
|
* A trait method whose body contains `parent::` calls is compiled with an implicit |
||||||
|
* `zend_class_entry *trait_parent_ce` parameter (so `parent::` can be bound to the |
||||||
|
* class that composes the trait). The shared `func_decl.h` declaration must emit the |
||||||
|
* same parameter; otherwise the generated C++ fails to compile with C2660 |
||||||
|
* ("function does not accept 3 arguments") at the call site that forwards to the trait |
||||||
|
* function. This is a code-generation-level check that fails before the fix and passes |
||||||
|
* after it. |
||||||
|
*/ |
||||||
|
class TraitFuncDeclTest extends \BaseTest |
||||||
|
{ |
||||||
|
public function testAliasedTraitConstructorParentCallDeclaresTraitParentCe(): void |
||||||
|
{ |
||||||
|
// BaseTest::compile() populates the global $translator and translates the file. |
||||||
|
$this->compile('trait-aliased-constructor-parent-call.php'); |
||||||
|
|
||||||
|
global $translator; |
||||||
|
$compiler = $translator; |
||||||
|
|
||||||
|
$headerPath = $compiler->getIncludeDir() . '/php_trait_parent_ce_func_decl.h'; |
||||||
|
if (file_exists($headerPath)) { |
||||||
|
@unlink($headerPath); |
||||||
|
} |
||||||
|
// Emit the shared function-declaration header (genFunctionDeclarations), which |
||||||
|
// is what the fix targets. |
||||||
|
$compiler->genFunctionDeclarations($headerPath); |
||||||
|
|
||||||
|
$decl = file_get_contents($headerPath); |
||||||
|
$this->assertMatchesRegularExpression( |
||||||
|
'/extern void php_tpdodriver____construct\([^;\n]*trait_parent_ce[^;\n]*\);/', |
||||||
|
$decl, |
||||||
|
'The trait function declaration must include its implicit parent scope' |
||||||
|
); |
||||||
|
$this->assertDoesNotMatchRegularExpression( |
||||||
|
'/extern void php_(?:driver__tpdodriverconstruct|directdriver____construct)' |
||||||
|
. '\([^;\n]*trait_parent_ce[^;\n]*\);/', |
||||||
|
$decl, |
||||||
|
'Composing-class wrapper declarations must not expose the implicit parent scope' |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,61 @@ |
|||||||
|
--TEST-- |
||||||
|
Trait parent constructor calls keep declarations aligned for aliased and direct wrappers |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
class Base |
||||||
|
{ |
||||||
|
public function __construct(array $options = []) |
||||||
|
{ |
||||||
|
var_dump($options); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
trait DriverConstructor |
||||||
|
{ |
||||||
|
public function __construct(array $options = []) |
||||||
|
{ |
||||||
|
$options['trait'] = 1; |
||||||
|
parent::__construct($options); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class AliasedDriver extends Base |
||||||
|
{ |
||||||
|
use DriverConstructor { |
||||||
|
__construct as private traitConstruct; |
||||||
|
} |
||||||
|
|
||||||
|
public function __construct(array $options = []) |
||||||
|
{ |
||||||
|
$options['alias'] = 1; |
||||||
|
$this->traitConstruct($options); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class DirectDriver extends Base |
||||||
|
{ |
||||||
|
use DriverConstructor; |
||||||
|
} |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
new AliasedDriver(['input' => 1]); |
||||||
|
new DirectDriver(['direct' => 1]); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
array(3) { |
||||||
|
["input"]=> |
||||||
|
int(1) |
||||||
|
["alias"]=> |
||||||
|
int(1) |
||||||
|
["trait"]=> |
||||||
|
int(1) |
||||||
|
} |
||||||
|
array(2) { |
||||||
|
["direct"]=> |
||||||
|
int(1) |
||||||
|
["trait"]=> |
||||||
|
int(1) |
||||||
|
} |
||||||
Loading…
Reference in new issue