Skip to content

Commit

Permalink
Fixed compilation error with inheritance of prototype interfaces
Browse files Browse the repository at this point in the history
Closes: #1758
  • Loading branch information
sergeyklay committed Dec 1, 2018
1 parent 98ef66f commit f239a03
Show file tree
Hide file tree
Showing 10 changed files with 291 additions and 58 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Expand Up @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]
### Fixed
- Fixed compilation error with inheritance of prototype interfaces
[#1758](https://github.com/phalcon/zephir/issues/1758)

## [0.11.7] - 2018-11-27
### Changed
Expand Down
102 changes: 70 additions & 32 deletions Library/ClassDefinition.php
Expand Up @@ -20,7 +20,7 @@
*
* Represents a class/interface and their properties and methods.
*/
class ClassDefinition
final class ClassDefinition
{
/** @var string */
protected $namespace;
Expand Down Expand Up @@ -49,11 +49,11 @@ class ClassDefinition
/** @var bool */
protected $external = false;

/** @var ClassDefinition */
/** @var ClassDefinitionRuntime|ClassDefinition */
protected $extendsClassDefinition;

/** @var ClassDefinition[] */
protected $implementedInterfaceDefinitions;
protected $implementedInterfaceDefinitions = [];

/** @var ClassProperty[] */
protected $properties = [];
Expand Down Expand Up @@ -350,7 +350,7 @@ public function getImplementedInterfaces()
/**
* Sets the class definition for the extended class.
*
* @param $classDefinition
* @param ClassDefinitionRuntime|ClassDefinition $classDefinition
*/
public function setExtendsClassDefinition($classDefinition)
{
Expand All @@ -360,7 +360,7 @@ public function setExtendsClassDefinition($classDefinition)
/**
* Returns the class definition related to the extended class.
*
* @return ClassDefinition
* @return ClassDefinition|ClassDefinitionRuntime
*/
public function getExtendsClassDefinition()
{
Expand Down Expand Up @@ -401,18 +401,13 @@ public function getImplementedInterfaceDefinitions()
public function getDependencies()
{
$dependencies = [];
if ($this->extendsClassDefinition) {
$classDefinition = $this->extendsClassDefinition;
if (method_exists($classDefinition, 'increaseDependencyRank')) {
$dependencies[] = $classDefinition;
}
if ($this->extendsClassDefinition && $this->extendsClassDefinition instanceof self) {
$dependencies[] = $this->extendsClassDefinition;
}

if ($this->implementedInterfaceDefinitions) {
foreach ($this->implementedInterfaceDefinitions as $interfaceDefinition) {
if (method_exists($interfaceDefinition, 'increaseDependencyRank')) {
$dependencies[] = $interfaceDefinition;
}
foreach ($this->implementedInterfaceDefinitions as $interfaceDefinition) {
if ($interfaceDefinition instanceof self) {
$dependencies[] = $interfaceDefinition;
}
}

Expand Down Expand Up @@ -699,10 +694,23 @@ public function hasMethod($methodName)
}

$extendsClassDefinition = $this->getExtendsClassDefinition();
if ($extendsClassDefinition instanceof self) {
if ($extendsClassDefinition instanceof ClassDefinitionRuntime) {
try {
$extendsClassDefinition = $this->compiler->getInternalClassDefinition(
$extendsClassDefinition->getName()
);
} catch (\ReflectionException $e) {
// Do nothing
return false;
}
}

while ($extendsClassDefinition instanceof self) {
if ($extendsClassDefinition->hasMethod($methodName)) {
return true;
}

$extendsClassDefinition = $extendsClassDefinition->getExtendsClassDefinition();
}

return false;
Expand Down Expand Up @@ -868,14 +876,32 @@ public function checkInterfaceImplements(self $classDefinition, self $interfaceD
{
foreach ($interfaceDefinition->getMethods() as $method) {
if (!$classDefinition->hasMethod($method->getName())) {
throw new CompilerException('Class '.$classDefinition->getCompleteName().' must implement a method called: "'.$method->getName().'" as requirement of interface: "'.$interfaceDefinition->getCompleteName().'"');
throw new CompilerException(
sprintf(
'Class %s must implement a method called: "%s" as requirement of interface: "%s"',
$classDefinition->getCompleteName(),
$method->getName(),
$interfaceDefinition->getCompleteName()
)
);
}

if ($method->hasParameters()) {
$implementedMethod = $classDefinition->getMethod($method->getName());
if ($implementedMethod->getNumberOfRequiredParameters() > $method->getNumberOfRequiredParameters() || $implementedMethod->getNumberOfParameters() < $method->getNumberOfParameters()) {
throw new CompilerException('Class '.$classDefinition->getCompleteName().'::'.$method->getName().'() does not have the same number of required parameters in interface: "'.$interfaceDefinition->getCompleteName().'"');
}
if (!$method->hasParameters()) {
continue;
}

$implementedMethod = $classDefinition->getMethod($method->getName());
if ($implementedMethod->getNumberOfRequiredParameters() > $method->getNumberOfRequiredParameters() ||
$implementedMethod->getNumberOfParameters() < $method->getNumberOfParameters()
) {
throw new CompilerException(
sprintf(
'Method %s::%s() does not have the same number of required parameters in interface: "%s"',
$classDefinition->getCompleteName(),
$method->getName(),
$interfaceDefinition->getCompleteName()
)
);
}
}
}
Expand Down Expand Up @@ -1008,12 +1034,12 @@ public function compile(CompilationContext $compilationContext)
*/
$compilationContext->classDefinition = $this;

/**
/*
* Get the global codePrinter.
*/
$codePrinter = $compilationContext->codePrinter;

/**
/*
* The ZEPHIR_INIT_CLASS defines properties and constants exported by the class.
*/
$initClassName = $this->getCNamespace().'_'.$this->getName();
Expand All @@ -1022,7 +1048,7 @@ public function compile(CompilationContext $compilationContext)

$codePrinter->increaseLevel();

/**
/*
* Method entry.
*/
$methods = &$this->methods;
Expand Down Expand Up @@ -1052,7 +1078,7 @@ public function compile(CompilationContext $compilationContext)
}
}

/**
/*
* Register the class with extends + interfaces.
*/
$classExtendsDefinition = null;
Expand Down Expand Up @@ -1082,8 +1108,6 @@ public function compile(CompilationContext $compilationContext)

/*
* Compile properties.
*
* @var ClassProperty
*/
foreach ($this->getProperties() as $property) {
$docBlock = $property->getDocBlock();
Expand All @@ -1102,8 +1126,6 @@ public function compile(CompilationContext $compilationContext)

/*
* Compile constants.
*
* @var ClassConstant
*/
foreach ($this->getConstants() as $constant) {
$docBlock = $constant->getDocBlock();
Expand Down Expand Up @@ -1142,9 +1164,25 @@ public function compile(CompilationContext $compilationContext)

if (!$classEntry) {
if ($compiler->isClass($interface)) {
throw new CompilerException('Cannot locate interface '.$interface.' when implementing interfaces on '.$this->getCompleteName().'. '.$interface.' is currently a class', $this->originalNode);
throw new CompilerException(
sprintf(
'Cannot locate interface %s when implementing interfaces on %s. '.
'%s is currently a class',
$interface,
$this->getCompleteName(),
$interface
),
$this->originalNode
);
} else {
throw new CompilerException('Cannot locate interface '.$interface.' when implementing interfaces on '.$this->getCompleteName(), $this->originalNode);
throw new CompilerException(
sprintf(
'Cannot locate interface %s when implementing interfaces on %s',
$interface,
$this->getCompleteName()
),
$this->originalNode
);
}
}

Expand Down

0 comments on commit f239a03

Please sign in to comment.