Zephir\ClassDefinition::compile PHP Method

compile() public method

Compiles a class/interface
public compile ( zephir\CompilationContext $compilationContext )
$compilationContext zephir\CompilationContext
    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;
    }