Permalink
Browse files

Call parent constructors if zephir is the origin of the childs constr…

…uctor

This should restore integrity in comparison with PHP behavior.

This calls the parent constructor explicitly,
if the user hasn't defined any (child) constructor.

This doesn't call the parent constructor,
if the user has defined a (child) constructor,
even if the child constructor doesn't do anything
(=empty statements).
  • Loading branch information...
steffen
steffen committed Jun 2, 2015
1 parent 400efed commit 67115ba4116b9a384e46fa3eb009d692643ed4f6
Showing with 75 additions and 16 deletions.
  1. +29 −1 Library/ClassDefinition.php
  2. +33 −3 Library/ClassMethod.php
  3. +13 −12 Library/ClassProperty.php
@@ -114,6 +114,13 @@ class ClassDefinition
* @var AliasManager
*/
protected $_aliasManager = null;
/**
* Whether the constructor was generated by zephir
* (-> no constructor existed previously)
* @var bool
*/
protected $isGeneratedConstructor = false;
/**
* ClassDefinition
@@ -148,7 +155,28 @@ public function isBundled()
{
return $this->isBundled;
}
/**
* Sets if the class constructor was generated by zephir
*
* @param boolean $isGeneratedConstructor
*/
public function setIsGeneratedConstructor($isGeneratedConstructor)
{
$this->isGeneratedConstructor = $isGeneratedConstructor;
}
/**
* Returns whether the constructor was generated by zephir
*
* @return bool
*/
public function isGeneratedConstructor()
{
return $this->isGeneratedConstructor;
}
/**
* Sets whether the class is external or not
*
@@ -1615,13 +1615,43 @@ public function compile(CompilationContext $compilationContext)
/**
* Set properties of the class, if constructor
*/
if ($this->getName() == '__construct' || ($this->getName() == 'unserialize' && in_array('Serializable', $this->classDefinition->getImplementedInterfaces()))) {
$initMethod = $this->classDefinition->getMethod('zephir_init_properties');
if ($initMethod && $initMethod->getClassDefinition() == $this->classDefinition) {
$classDefinition = $this->classDefinition;
if ($this->isConstructor() || ($this->getName() == 'unserialize' && in_array('Serializable', $classDefinition->getImplementedInterfaces()))) {
$initMethod = $classDefinition->getMethod('zephir_init_properties');
if ($initMethod && $initMethod->getClassDefinition() == $classDefinition) {
$codePrinter->increaseLevel();
/* Only initialize properties if the constructor was called directly on the class, e.g. make parent:: calls not trigger property initialization */
if ($this->isConstructor()) {
$codePrinter->output('if (EG(called_scope) == '.$classDefinition->getClassEntry().') {');
$codePrinter->increaseLevel();
}
$codePrinter->output('zephir_init_properties(this_ptr TSRMLS_CC);');
if ($this->isConstructor()) {
$codePrinter->decreaseLevel();
$codePrinter->output('}');
}
$codePrinter->decreaseLevel();
}
$extendsClass = $classDefinition->getExtendsClassDefinition();
$parentConstructor = $extendsClass ? $extendsClass->getMethod('__construct') : null;
if ($this->isConstructor() && $extendsClass && $parentConstructor && !$extendsClass->isGeneratedConstructor() && $classDefinition->isGeneratedConstructor()) {
/**
* Call the parent constructor (create parent::__construct call)
* since no constructor was actually defined for this class, but
* generated for zephir to ensure property initialization
*/
$callExpr = new Expression(array(
'type' => 'scall',
'dynamic-class' => 0,
'class' => 'parent',
'dynamic' => 0,
'name' => '__construct',
));
$call = new StaticCall();
$expr = $callExpr;
$expr->setExpectReturn(false);
$call->compile($expr, $compilationContext);
}
}
/**
@@ -248,7 +248,7 @@ public function compile(CompilationContext $compilationContext)
case 'array':
case 'empty-array':
$classDefinition = $this->classDefinition;
if ($this->isStatic()) {
$methodName = 'zephir_init_static_properties';
$visibility = array('internal');
@@ -257,26 +257,27 @@ public function compile(CompilationContext $compilationContext)
$visibility = array('internal');
/* Make sure a constructor exists, so that properties are initialized */
$method = $compilationContext->classDefinition->getMethod('__construct');
if (!$method || $method->getClassDefinition() != $this->classDefinition) {
$this->classDefinition->addMethod(new ClassMethod(
$compilationContext->classDefinition,
$method = $classDefinition->getMethod('__construct');
if (!$method || $method->getClassDefinition() != $classDefinition) {
$classDefinition->setIsGeneratedConstructor(true);
$classDefinition->getEventsManager()->dispatch('setMethod', array(new ClassMethod(
$classDefinition,
array('public'),
'__construct',
null,
null
), null);
), null));
}
}
$constructParentMethod = null;
$constructMethod = $compilationContext->classDefinition->getMethod($methodName);
$constructMethod = $classDefinition->getMethod($methodName);
/**
* Make sure we do not steal the construct method of the parent,
* but initialize our own with the inherited statements to ensure
* valid property initialization
*/
if ($constructMethod && $constructMethod->getClassDefinition() != $this->classDefinition) {
if ($constructMethod && $constructMethod->getClassDefinition() != $classDefinition) {
$constructParentMethod = $constructMethod;
$constructMethod = null;
}
@@ -308,12 +309,12 @@ public function compile(CompilationContext $compilationContext)
$statementsBlock->setStatements($newStatements);
$constructMethod->setStatementsBlock($statementsBlock);
$compilationContext->classDefinition->getEventsManager()->dispatch('setMethod', array($constructMethod));
$classDefinition->getEventsManager()->dispatch('setMethod', array($constructMethod));
}
} else {
$statementsBlockBuilder = new StatementsBlockBuilder(array($this->getLetStatement()), false);
$constructMethod->setStatementsBlock(new StatementsBlock($statementsBlockBuilder->get()));
$compilationContext->classDefinition->getEventsManager()->dispatch('setMethod', array($constructMethod));
$classDefinition->getEventsManager()->dispatch('setMethod', array($constructMethod));
}
} else {
$statements = array();
@@ -323,8 +324,8 @@ public function compile(CompilationContext $compilationContext)
$statements[] = $this->getLetStatement()->get();
$statementsBlock = new StatementsBlock($statements);
$compilationContext->classDefinition->getEventsManager()->dispatch('setMethod', array(new ClassMethod(
$compilationContext->classDefinition,
$classDefinition->getEventsManager()->dispatch('setMethod', array(new ClassMethod(
$classDefinition,
$visibility,
$methodName,
null,

0 comments on commit 67115ba

Please sign in to comment.