public function compile(CompilationContext $compilationContext)
{
$this->compiler = $compilationContext->compiler;
/**
* Sets the current object as global class definition
*/
$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();
$codePrinter->output('ZEPHIR_INIT_CLASS(' . $initClassName . ') {');
$codePrinter->outputBlankLine();
$codePrinter->increaseLevel();
/**
* Method entry
*/
$methods =& $this->methods;
$initMethod = $this->getLocalOrParentInitMethod();
if (count($methods) || $initMethod) {
$methodEntry = strtolower($this->getCNamespace()) . '_' . strtolower($this->getName()) . '_method_entry';
} else {
$methodEntry = 'NULL';
}
foreach ($methods as $method) {
$method->setupOptimized($compilationContext);
}
$namespace = str_replace('\\', '_', $compilationContext->config->get('namespace'));
$flags = '0';
if ($this->isAbstract()) {
$flags = 'ZEND_ACC_EXPLICIT_ABSTRACT_CLASS';
}
if ($this->isFinal()) {
if ($flags == '0') {
$flags = 'ZEND_ACC_FINAL_CLASS';
} else {
$flags .= '|ZEND_ACC_FINAL_CLASS';
}
}
/**
* Register the class with extends + interfaces
*/
$classExtendsDefinition = null;
if ($this->extendsClass) {
$classExtendsDefinition = $this->extendsClassDefinition;
if ($classExtendsDefinition instanceof ClassDefinition && !$classExtendsDefinition->isBundled()) {
$classEntry = $classExtendsDefinition->getClassEntry($compilationContext);
} else {
$classEntry = $this->getClassEntryByClassName($classExtendsDefinition->getName(), $compilationContext);
}
if ($this->getType() == 'class') {
$codePrinter->output('ZEPHIR_REGISTER_CLASS_EX(' . $this->getNCNamespace() . ', ' . $this->getName() . ', ' . $namespace . ', ' . strtolower($this->getSCName($namespace)) . ', ' . $classEntry . ', ' . $methodEntry . ', ' . $flags . ');');
$codePrinter->outputBlankLine();
} else {
$codePrinter->output('ZEPHIR_REGISTER_INTERFACE_EX(' . $this->getNCNamespace() . ', ' . $this->getName() . ', ' . $namespace . ', ' . strtolower($this->getSCName($namespace)) . ', ' . $classEntry . ', ' . $methodEntry . ');');
$codePrinter->outputBlankLine();
}
} else {
if ($this->getType() == 'class') {
$codePrinter->output('ZEPHIR_REGISTER_CLASS(' . $this->getNCNamespace() . ', ' . $this->getName() . ', ' . $namespace . ', ' . strtolower($this->getSCName($namespace)) . ', ' . $methodEntry . ', ' . $flags . ');');
} else {
$codePrinter->output('ZEPHIR_REGISTER_INTERFACE(' . $this->getNCNamespace() . ', ' . $this->getName() . ', ' . $namespace . ', ' . strtolower($this->getSCName($namespace)) . ', ' . $methodEntry . ');');
}
$codePrinter->outputBlankLine();
}
/**
* Compile properties
* @var $property ClassProperty
*/
foreach ($this->getProperties() as $property) {
$docBlock = $property->getDocBlock();
if ($docBlock) {
$codePrinter->outputDocBlock($docBlock, true);
}
$property->compile($compilationContext);
$codePrinter->outputBlankLine();
}
$initMethod = $this->getInitMethod();
if ($initMethod) {
$codePrinter->output($namespace . '_' . strtolower($this->getSCName($namespace)) . '_ce->create_object = ' . $initMethod->getName() . ';');
}
/**
* Compile constants
* @var $constant ClassConstant
*/
foreach ($this->getConstants() as $constant) {
$docBlock = $constant->getDocBlock();
if ($docBlock) {
$codePrinter->outputDocBlock($docBlock, true);
}
$constant->compile($compilationContext);
$codePrinter->outputBlankLine();
}
/**
* Implemented interfaces
*/
$interfaces = $this->interfaces;
$compiler = $compilationContext->compiler;
if (is_array($interfaces)) {
$codePrinter->outputBlankLine(true);
foreach ($interfaces as $interface) {
/**
* Try to find the interface
*/
$classEntry = false;
if ($compiler->isInterface($interface)) {
$classInterfaceDefinition = $compiler->getClassDefinition($interface);
$classEntry = $classInterfaceDefinition->getClassEntry($compilationContext);
} else {
if ($compiler->isBundledInterface($interface)) {
$classInterfaceDefinition = $compiler->getInternalClassDefinition($interface);
$classEntry = $this->getClassEntryByClassName($classInterfaceDefinition->getName(), $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);
} else {
throw new CompilerException("Cannot locate interface " . $interface . " when implementing interfaces on " . $this->getCompleteName(), $this->originalNode);
}
}
/**
* We don't check if abstract classes implement the methods in their interfaces
*/
if (!$this->isAbstract() && !$this->isInterface()) {
$this->checkInterfaceImplements($this, $classInterfaceDefinition);
}
$codePrinter->output('zend_class_implements(' . $this->getClassEntry() . ' TSRMLS_CC, 1, ' . $classEntry . ');');
}
}
if (!$this->isAbstract() && !$this->isInterface()) {
/**
* Interfaces in extended classes may have
*/
if ($classExtendsDefinition) {
if ($classExtendsDefinition instanceof ClassDefinition && !$classExtendsDefinition->isBundled()) {
$interfaces = $classExtendsDefinition->getImplementedInterfaces();
if (is_array($interfaces)) {
foreach ($interfaces as $interface) {
$classInterfaceDefinition = null;
if ($compiler->isInterface($interface)) {
$classInterfaceDefinition = $compiler->getClassDefinition($interface);
} else {
if ($compiler->isBundledInterface($interface)) {
$classInterfaceDefinition = $compiler->getInternalClassDefinition($interface);
}
}
if ($classInterfaceDefinition) {
$this->checkInterfaceImplements($this, $classInterfaceDefinition);
}
}
}
}
}
}
$codePrinter->output('return SUCCESS;');
$codePrinter->outputBlankLine();
$codePrinter->decreaseLevel();
$codePrinter->output('}');
$codePrinter->outputBlankLine();
/**
* Compile methods
*/
foreach ($methods as $method) {
$docBlock = $method->getDocBlock();
if ($docBlock) {
$codePrinter->outputDocBlock($docBlock);
}
if ($this->getType() == 'class') {
if (!$method->isInternal()) {
$codePrinter->output('PHP_METHOD(' . $this->getCNamespace() . '_' . $this->getName() . ', ' . $method->getName() . ') {');
} else {
$codePrinter->output($compilationContext->backend->getInternalSignature($method, $compilationContext) . ' {');
}
$codePrinter->outputBlankLine();
if (!$method->isAbstract()) {
$method->compile($compilationContext);
}
$codePrinter->output('}');
$codePrinter->outputBlankLine();
} else {
$codePrinter->output('ZEPHIR_DOC_METHOD(' . $this->getCNamespace() . '_' . $this->getName() . ', ' . $method->getName() . ');');
$codePrinter->outputBlankLine();
}
}
/**
* Check whether classes must be exported
*/
$exportClasses = $compilationContext->config->get('export-classes', 'extra');
if ($exportClasses) {
$exportAPI = 'extern ZEPHIR_API';
} else {
$exportAPI = 'extern';
}
/**
* Create a code printer for the header file
*/
$codePrinter = new CodePrinter();
$codePrinter->outputBlankLine();
$codePrinter->output($exportAPI . ' zend_class_entry *' . $this->getClassEntry() . ';');
$codePrinter->outputBlankLine();
$codePrinter->output('ZEPHIR_INIT_CLASS(' . $this->getCNamespace() . '_' . $this->getName() . ');');
$codePrinter->outputBlankLine();
if ($this->getType() == 'class') {
if (count($methods)) {
foreach ($methods as $method) {
if (!$method->isInternal()) {
$codePrinter->output('PHP_METHOD(' . $this->getCNamespace() . '_' . $this->getName() . ', ' . $method->getName() . ');');
} else {
$internalSignature = $compilationContext->backend->getInternalSignature($method, $compilationContext);
$codePrinter->output($internalSignature . ';');
}
}
$codePrinter->outputBlankLine();
}
}
/**
* Create argument info
*/
foreach ($methods as $method) {
$parameters = $method->getParameters();
if (count($parameters)) {
$codePrinter->output('ZEND_BEGIN_ARG_INFO_EX(arginfo_' . strtolower($this->getCNamespace() . '_' . $this->getName() . '_' . $method->getName()) . ', 0, 0, ' . $method->getNumberOfRequiredParameters() . ')');
foreach ($parameters->getParameters() as $parameter) {
switch ($parameter['data-type']) {
case 'array':
$codePrinter->output("\t" . 'ZEND_ARG_ARRAY_INFO(0, ' . $parameter['name'] . ', ' . (isset($parameter['default']) ? 1 : 0) . ')');
break;
case 'variable':
if (isset($parameter['cast'])) {
switch ($parameter['cast']['type']) {
case 'variable':
$value = $parameter['cast']['value'];
$codePrinter->output("\t" . 'ZEND_ARG_OBJ_INFO(0, ' . $parameter['name'] . ', ' . Utils::escapeClassName($compilationContext->getFullName($value)) . ', ' . (isset($parameter['default']) ? 1 : 0) . ')');
break;
default:
throw new Exception('Unexpected exception');
}
} else {
$codePrinter->output("\t" . 'ZEND_ARG_INFO(0, ' . $parameter['name'] . ')');
}
break;
default:
$codePrinter->output("\t" . 'ZEND_ARG_INFO(0, ' . $parameter['name'] . ')');
break;
}
}
$codePrinter->output('ZEND_END_ARG_INFO()');
$codePrinter->outputBlankLine();
}
}
if (count($methods)) {
$codePrinter->output('ZEPHIR_INIT_FUNCS(' . strtolower($this->getCNamespace() . '_' . $this->getName()) . '_method_entry) {');
foreach ($methods as $method) {
$parameters = $method->getParameters();
if ($this->getType() == 'class') {
if (!$method->isInternal()) {
if (count($parameters)) {
$codePrinter->output("\t" . 'PHP_ME(' . $this->getCNamespace() . '_' . $this->getName() . ', ' . $method->getName() . ', arginfo_' . strtolower($this->getCNamespace() . '_' . $this->getName() . '_' . $method->getName()) . ', ' . $method->getModifiers() . ')');
} else {
$codePrinter->output("\t" . 'PHP_ME(' . $this->getCNamespace() . '_' . $this->getName() . ', ' . $method->getName() . ', NULL, ' . $method->getModifiers() . ')');
}
}
} else {
if ($method->isStatic()) {
if (count($parameters)) {
$codePrinter->output("\t" . 'ZEND_FENTRY(' . $method->getName() . ', NULL, arginfo_' . strtolower($this->getCNamespace() . '_' . $this->getName() . '_' . $method->getName()) . ', ZEND_ACC_STATIC|ZEND_ACC_ABSTRACT|ZEND_ACC_PUBLIC)');
} else {
$codePrinter->output("\t" . 'ZEND_FENTRY(' . $method->getName() . ', NULL, NULL, ZEND_ACC_STATIC|ZEND_ACC_ABSTRACT|ZEND_ACC_PUBLIC)');
}
} else {
if (count($parameters)) {
$codePrinter->output("\t" . 'PHP_ABSTRACT_ME(' . $this->getCNamespace() . '_' . $this->getName() . ', ' . $method->getName() . ', arginfo_' . strtolower($this->getCNamespace() . '_' . $this->getName() . '_' . $method->getName()) . ')');
} else {
$codePrinter->output("\t" . 'PHP_ABSTRACT_ME(' . $this->getCNamespace() . '_' . $this->getName() . ', ' . $method->getName() . ', NULL)');
}
}
}
}
$codePrinter->output("\t" . 'PHP_FE_END');
$codePrinter->output('};');
}
$compilationContext->headerPrinter = $codePrinter;
}