public function scan() { require_once $this->path; $ns = ""; $_ns = ""; $ns_bracket = false; $aliases = []; $tokens = new Tokenizer(FS::get($this->path)); while ($tokens->valid()) { if ($tokens->is(T_NAMESPACE)) { $ns = ""; $_ns = ""; $tokens->next(); if ($tokens->is(T_STRING)) { $ns = $this->_parseName($tokens); if ($tokens->is('{')) { $tokens->skip(); $ns_bracket = true; } else { $tokens->skipIf(';'); } $_ns = $ns . '\\'; } elseif ($tokens->is('{')) { $ns_bracket = true; $tokens->next(); } } elseif ($tokens->is(T_USE)) { do { $tokens->next(); $name = $this->_parseName($tokens); if ($tokens->is(T_AS)) { $aliases[$tokens->next()->get(T_STRING)] = $name; $tokens->next(); } else { if (strpos($name, '\\') === false) { $aliases[$name] = $name; } else { $aliases[ltrim('\\', strrchr($name, '\\'))] = $name; } } } while ($tokens->is(',')); $tokens->need(';')->next(); } elseif ($tokens->is(T_CONST)) { $name = $tokens->next()->get(T_STRING); $constant = new EntityConstant($_ns . $name); $constant->setValue(constant($_ns . $name)); $constant->setLine($this->line($tokens->getLine())); $this->constants[$_ns . $name] = $constant; $tokens->forwardTo(';')->next(); } elseif ($tokens->is(T_FUNCTION)) { $name = $tokens->next()->get(T_STRING); $function = new EntityFunction($_ns . $name); $function->setLine($this->line($tokens->getLine())); $function->setAliases($aliases); $this->parseCallable($function, new \ReflectionFunction($function->name)); $function->setBody($tokens->forwardTo('{')->getScope()); $tokens->next(); $this->functions[$function->name] = $function; } elseif ($tokens->is(T_FINAL, T_ABSTRACT, T_INTERFACE, T_TRAIT, T_CLASS)) { $tokens->forwardTo(T_STRING); $name = $tokens->current(); $class = new EntityClass($_ns . $name); $ref = new \ReflectionClass($class->name); $doc = $ref->getDocComment(); // if($name == "NamesInterface") { // drop($ref); // } if ($ref->isInterface()) { $class->addFlag(Flags::IS_INTERFACE); } elseif ($ref->isTrait()) { $class->addFlag(Flags::IS_TRAIT); } else { $class->addFlag(Flags::IS_CLASS); } if ($ref->isAbstract()) { $class->addFlag(Flags::IS_ABSTRACT); } elseif ($ref->isFinal()) { $class->addFlag(Flags::IS_FINAL); } if ($doc) { $info = ToolKit::parseDoc($doc); $class->setDescription($info['desc']); $class->addOptions($info['options']); } $class->setAliases($aliases); $class->setLine($this->line($tokens->getLine())); $tokens->next(); if ($tokens->is(T_EXTENDS)) { // process 'extends' keyword do { $tokens->next(); $root = $tokens->is(T_NS_SEPARATOR); $parent = $this->_parseName($tokens); if ($root) { // extends from root namespace $class->setParent($parent, $class->isInterface()); } elseif (isset($aliases[$parent])) { $class->setParent($aliases[$parent], $class->isInterface()); } else { $class->setParent($_ns . $parent, $class->isInterface()); } } while ($tokens->is(',')); } if ($tokens->is(T_IMPLEMENTS)) { // process 'implements' keyword do { $tokens->next(); $root = $tokens->is(T_NS_SEPARATOR); $parent = $this->_parseName($tokens); if ($root) { // extends from root namespace $class->addInterface($parent); } elseif (isset($aliases[$parent])) { $class->addInterface($aliases[$parent]); } else { $class->addInterface($_ns . $parent); } } while ($tokens->is(',')); } $tokens->forwardTo('{')->next(); while ($tokens->forwardTo(T_CONST, T_FUNCTION, '{', '}', T_VARIABLE) && $tokens->valid()) { switch ($tokens->key()) { case T_CONST: $constant = new EntityConstant($class->name . '::' . $tokens->next()->get(T_STRING)); $constant->setValue(constant($constant->name)); $constant->setLine(new Line($this, $tokens->getLine())); $class->addConstant($constant); break; case T_VARIABLE: $property = new EntityProperty(ltrim($tokens->getAndNext(), '$')); $ref = new \ReflectionProperty($class->name, $property->name); $doc = $ref->getDocComment(); if ($doc) { $property->setDescription(ToolKit::parseDoc($doc)['desc']); } if ($ref->isPrivate()) { $property->addFlag(Flags::IS_PRIVATE); } elseif ($ref->isProtected()) { $property->addFlag(Flags::IS_PROTECTED); } else { $property->addFlag(Flags::IS_PUBLIC); } if ($ref->isStatic()) { $property->addFlag(Flags::IS_STATIC); } if ($ref->isDefault()) { $property->setValue($ref->getDeclaringClass()->getDefaultProperties()[$property->name]); } $class->addProperty($property); break; case T_FUNCTION: $method = new EntityMethod($name . '::' . $tokens->next()->get(T_STRING)); $method->setLine($this->line($tokens->getLine())); $this->parseCallable($method, $ref = new \ReflectionMethod($class->name, $method->short)); if ($ref->isPrivate()) { $method->addFlag(Flags::IS_PRIVATE); } elseif ($ref->isProtected()) { $method->addFlag(Flags::IS_PROTECTED); } else { $method->addFlag(Flags::IS_PUBLIC); } if ($ref->isStatic()) { $method->addFlag(Flags::IS_STATIC); } if ($ref->isAbstract()) { $method->addFlag(Flags::IS_ABSTRACT); $method->addFlag(Flags::IS_ABSTRACT_IMPLICIT); } elseif ($ref->isFinal()) { $method->addFlag(Flags::IS_FINAL); } if (isset($method->options['deprecated'])) { $method->addFlag(Flags::IS_DEPRECATED); } $tokens->forwardTo(')')->next(); if ($tokens->is('{')) { $method_body = $tokens->getScope(); $method->setBody($method_body); } $tokens->next(); $class->addMethod($method); break; case '{': // use traits scope $tokens->forwardTo('}')->next(); break; case '}': // end of class $tokens->next(); $this->classes[$class->name] = $class; break 2; } } } elseif ($tokens->is('}') && $ns_bracket) { $tokens->next(); $ns_bracket = false; } else { drop($tokens->curr); if ($tokens->valid()) { throw new UnexpectedTokenException($tokens); } break; } } }
/** * @param EntityClass $class * @return string */ public function classC(EntityClass $class) { $escaped = addslashes($class->name); $name = $class->cname; $path = str_replace('\\', '/', $class->name); ob_start(); echo <<<TOP /* Extension */ #include "php.h" #include "koda_helper.h" #include "{$path}.h" zend_class_entry *ce_{$name}; zend_object_handlers handlers_{$name}; BEGIN_EXTERN_C(); TOP; if ($class->methods) { $method_table = []; foreach ($class->methods as $method) { if ($method->isAbstract()) { $method_table[] = "ZEND_FENTRY({$method->short}, NULL, arginfo_{$method->short}, {$this->_meFlags($method)})"; } else { $method_table[] = "ZEND_ME({$name}, {$method->short}, arginfo_{$method->short}, {$this->_meFlags($method)})"; } if ($method->arguments) { $arginfo = []; foreach ($method->arguments as $argument) { $arginfo[] = $this->_arginfo($argument) . " // {$argument->dump()}"; } $arginfo = "\n " . implode("\n ", $arginfo); } else { $arginfo = ""; } if (!$method->isAbstract()) { $scope = new Scope($method); $body = $scope->convert(); echo <<<METHOD /* proto {$method->dump()} */ PHP_METHOD({$name}, {$method->short}) { {$body} } METHOD; } echo <<<METHOD ZEND_BEGIN_ARG_INFO_EX(arginfo_{$method->short}, 0, {$method->isReturnRef()}, {$method->required}){$arginfo} ZEND_END_ARG_INFO(); METHOD; } $method_table = implode("\n ", $method_table); } else { $method_table = ""; } echo <<<REGISTER_METHODS /* Register methods */ static const zend_function_entry {$name}_methods[] = { {$method_table} {NULL, NULL, NULL} }; REGISTER_METHODS; if ($class->constants) { $constants = ["/* Class constants */"]; foreach ($class->constants as $constant) { $constants[] = "/* {$constant->dump()} */"; $constants[] = $this->_constant($constant); } $constants = "\n " . implode("\n ", $constants) . "\n"; } else { $constants = ""; } if ($class->properties) { $properties = ["/* Class properties */"]; foreach ($class->properties as $prop) { $properties[] = "/* {$prop->dump()} */"; $properties[] = $this->_property($prop); } $properties = "\n " . implode("\n ", $properties) . "\n"; } else { $properties = ""; } $register = $inherit = []; if ($class->flags & Flags::IS_INTERFACE) { $register[] = "ce_{$name} = zend_register_internal_interface(&ce TSRMLS_CC);"; if ($class->parents) { foreach ($class->parents as $parent) { $inherit[] = "if(!kd_extend_class(ce_{$name} TSRMLS_CC, {$parent->quote('strtolower')})) {"; $inherit[] = " zend_error(E_CORE_ERROR, \"{$this->project->name}: {$class->getEscapedName()} can't extends {$parent->getEscapedName()}\");"; $inherit[] = " return FAILURE;"; $inherit[] = "}"; } } } else { $register[] = "ce_{$name} = zend_register_internal_class(&ce TSRMLS_CC);"; if ($class->parent) { $inherit[] = "if(!kd_extend_class(ce_{$name} TSRMLS_CC, {$class->parent->quote('strtolower')})) {"; $inherit[] = " zend_error(E_CORE_ERROR, \"{$this->project->name}: {$class->getEscapedName()} can't extends {$class->parent->getEscapedName()}\");"; $inherit[] = " return FAILURE;"; $inherit[] = "}"; } else { $register[] = "memcpy(&handlers_{$name}, zend_get_std_object_handlers(), sizeof(zend_object_handlers));"; } if ($class->flags & Flags::IS_ABSTRACT) { $register[] = "ce_{$name}->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS;"; } if ($class->flags & Flags::IS_ABSTRACT_IMPLICIT) { $register[] = "ce_{$name}->ce_flags |= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS;"; } if ($class->flags & Flags::IS_FINAL) { $register[] = "ce_{$name}->ce_flags |= ZEND_ACC_FINAL_CLASS;"; } } if ($class->interfaces) { $interfaces = implode('", "', array_map('strtolower', array_map('addslashes', array_keys($class->interfaces)))); $inherit[] = "kd_implements_class(ce_{$name} TSRMLS_CC, " . count($class->interfaces) . ", \"{$interfaces}\");"; } $register = implode("\n ", $register); $inherit = implode("\n ", $inherit); echo <<<REGISTER_CLASS /* Init class */ PHP_MINIT_FUNCTION(init_{$name}) { zend_class_entry ce; /* Init class entry */ INIT_CLASS_ENTRY(ce, "{$escaped}", {$name}_methods); {$register} {$constants} {$properties} return SUCCESS; } /* Extending and implementing */ PHP_MINIT_FUNCTION(load_{$name}) { {$inherit} return SUCCESS; } END_EXTERN_C(); REGISTER_CLASS; return ob_get_clean(); }