public function advance($parser) { if ($this->pos >= 0) { $type = $this->tokens[$this->pos][0]; switch ($type) { case T_FOR: case T_FOREACH: $this->start_loop(); break; case T_DO: $this->ignoreNextWhile = true; $this->start_loop(); break; case T_WHILE: if (!$this->ignoreNextWhile) { $this->start_loop(); } $this->ignoreNextWhile = false; break; case T_SWITCH: case T_TRY: $this->start_cond(); break; case T_IF: if (!$this->lastWasElse) { $this->start_cond(); } else { // count the number of "T_ELSE T_IF" because for each of those we get another call // to end_cond() $this->vars->set_elseif(); } break; case T_ELSEIF: $this->start_cond(true, false); break; case T_ELSE: $this->start_cond(true, true); break; } $this->lastWasElse = $type == T_ELSE; } $res = parent::advance($parser); if ($this->lastComment && $this->lastComment != $this->lastCheckComment) { // it was a comment, so lets see if it contains a "@var $<name> <type>" that gives us a // hint what type a variable has. $matches = array(); if (preg_match('/\\@var\\s+\\$([a-z0-9_]+)\\s+(\\S+)/i', $this->lastComment, $matches)) { // do we know that variable? $scopename = $this->scope->get_name(); if ($this->vars->exists($scopename, $matches[1])) { // ok, determine type and set it $type = PC_Obj_MultiType::get_type_by_name($matches[2]); $var = $this->vars->get($scopename, $matches[1]); $var->set_type($type); } } $this->lastCheckComment = $this->lastComment; } return $res; }
public function test_oop() { list(, $classes, $vars, $calls, $errors) = $this->analyze(self::$code); self::assert_equals(0, count($errors)); $a = $classes['a']; /* @var $a PC_Obj_Class */ self::assert_equals(false, $a->is_abstract()); self::assert_equals(false, $a->is_interface()); self::assert_equals(false, $a->is_final()); self::assert_equals(null, $a->get_super_class()); self::assert_equals(array(), $a->get_interfaces()); self::assert_equals((string) PC_Obj_MultiType::create_int(0), (string) $a->get_constant('c')->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_int(4), (string) $a->get_constant('ME')->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_string('str'), (string) $a->get_constant('YOU')->get_type()); self::assert_equals((string) new PC_Obj_Field('', 0, 'f', PC_Obj_MultiType::create_string('abc'), PC_Obj_Field::V_PRIVATE), (string) $a->get_field('f')); self::assert_equals((string) new PC_Obj_Field('', 0, 'foo', PC_Obj_MultiType::get_type_by_name('int|string'), PC_Obj_Field::V_PRIVATE), (string) $a->get_field('foo')); self::assert_equals((string) new PC_Obj_Field('', 0, 'bar', PC_Obj_MultiType::get_type_by_name('int|string'), PC_Obj_Field::V_PRIVATE), (string) $a->get_field('bar')); self::assert_equals((string) new PC_Obj_Field('', 0, 'a', PC_Obj_MultiType::create_string(), PC_Obj_Field::V_PRIVATE), (string) $a->get_field('a')); self::assert_equals((string) new PC_Obj_Field('', 0, 'b', PC_Obj_MultiType::create_string('a'), PC_Obj_Field::V_PRIVATE), (string) $a->get_field('b')); $array = PC_Obj_MultiType::create_array(array()); $array->get_first()->set_array_type(0, PC_Obj_MultiType::create_int(1)); $array->get_first()->set_array_type(1, PC_Obj_MultiType::create_int(2)); $array->get_first()->set_array_type(2, PC_Obj_MultiType::create_int(3)); self::assert_equals((string) new PC_Obj_Field('', 0, 'p', $array, PC_Obj_Field::V_PROTECTED), (string) $a->get_field('p')); self::assert_equals('public function __construct()', (string) $a->get_method('__construct')); self::assert_equals('protected function test(): void', (string) $a->get_method('test')); self::assert_equals('public function test2(a): a', (string) $a->get_method('test2')); $b = $classes['b']; /* @var $b PC_Obj_Class */ self::assert_equals(true, $b->is_abstract()); self::assert_equals(false, $b->is_interface()); self::assert_equals(false, $b->is_final()); self::assert_equals('a', $b->get_super_class()); self::assert_equals(array('i', 'j'), $b->get_interfaces()); self::assert_equals((string) PC_Obj_MultiType::create_int(0), (string) $b->get_constant('c')->get_type()); self::assert_equals(null, $b->get_field('f')); self::assert_equals((string) new PC_Obj_Field('', 0, 'p', $array, PC_Obj_Field::V_PROTECTED), (string) $b->get_field('p')); $i = $classes['i']; /* @var $i PC_Obj_Class */ self::assert_equals(true, $i->is_abstract()); self::assert_equals(true, $i->is_interface()); self::assert_equals(false, $i->is_final()); self::assert_equals(array('i1', 'i2'), $i->get_interfaces()); self::assert_equals('public abstract function doSomething(): string', (string) $i->get_method('doSomething')); $x = $classes['x']; /* @var $x PC_Obj_Class */ self::assert_equals(false, $x->is_abstract()); self::assert_equals(false, $x->is_interface()); self::assert_equals(true, $x->is_final()); self::assert_equals('b', $x->get_super_class()); self::assert_equals(array('i'), $x->get_interfaces()); self::assert_equals('public function doSomething(): string', (string) $x->get_method('doSomething')); self::assert_equals('public function test2(b): b', (string) $x->get_method('test2')); self::assert_equals('public static function mystatic(): void', (string) $x->get_method('mystatic')); $field = new PC_Obj_Field('', 0, 'var', PC_Obj_MultiType::create_int(4), PC_Obj_Field::V_PRIVATE); $field->set_static(true); self::assert_equals((string) $field, (string) $x->get_field('var')); $global = $vars[PC_Obj_Variable::SCOPE_GLOBAL]; self::assert_equals((string) PC_Obj_MultiType::create_int(0), (string) $global['a']->get_type()); self::assert_equals('a', (string) $global['b']->get_type()); self::assert_equals('a', (string) $global['c']->get_type()); self::assert_equals('x', (string) $global['d']->get_type()); self::assert_equals('b', (string) $global['e']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_int(4), (string) $global['f']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_int(1), (string) $global['g']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_int(4), (string) $global['h']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_object('b'), (string) $global['i']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_int(), (string) $global['j']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_string(), (string) $global['n']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_int(95), (string) $global['o']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_object('a'), (string) $global['q']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_object('b'), (string) $global['r']->get_type()); // check calls $i = 0; self::assert_equals((string) $calls[$i++], 'strstr(integer=4, string=str)'); self::assert_call($calls[$i++], 'b', 'get42', true); self::assert_call($calls[$i++], 'a', 'test', false); self::assert_equals((string) $calls[$i++], 'dummy(integer=2)'); self::assert_equals((string) $calls[$i++], 'dummy(integer=3)'); self::assert_equals((string) $calls[$i++], 'dummy(integer=6)'); self::assert_equals((string) $calls[$i++], 'dummy(unknown)'); self::assert_equals((string) $calls[$i++], 'dummy(unknown)'); self::assert_call($calls[$i++], 'a', '__construct', false); self::assert_call($calls[$i++], 'a', 'test2', false); self::assert_call($calls[$i++], 'x', '__construct', false); self::assert_call($calls[$i++], 'x', 'test2', false); self::assert_call($calls[$i++], 'x', 'test2', false); self::assert_call($calls[$i++], 'b', 'test2', false); self::assert_call($calls[$i++], 'b', 'sdf', true); self::assert_call($calls[$i++], 'x', 'partest', false); self::assert_call($calls[$i++], 'a', '__construct', false); self::assert_call($calls[$i++], 'x', '__construct', false); self::assert_call($calls[$i++], 'a', 'test2', false); self::assert_call($calls[$i++], 'x', 'test2', false); }
/** * Parses the given method-phpdoc * * @param PC_Obj_Method $func the method to which the phpdoc belongs */ public function parse_method_doc($func) { if (isset($this->funcComments[$func->get_name()])) { $doc = $this->funcComments[$func->get_name()]; // look for params $matches = array(); preg_match_all('/\\@param\\s+([^\\s]+)\\s+(&)?\\s*([^\\s]+)/', $doc, $matches); foreach ($matches[1] as $k => $match) { $param = substr($matches[3][$k], 1); // does the param exist? if (($fp = $func->get_param($param)) !== null) { $mtype = PC_Obj_MultiType::get_type_by_name($match); $fptype = $fp->get_mtype(); $isref = $matches[2][$k] != ''; // don't report that if we only know the type from the default value if ($fp->is_reference() != $isref || !$fp->is_mtype_default() && !$fptype->is_unknown() && !$fptype->equals($mtype)) { $docstr = ($isref ? '&' : '') . $mtype; $this->report_error('PHPDoc (' . $docstr . ') does not match the parameter $' . $param . ' (' . $fp . ')', PC_Obj_Error::E_T_PARAM_DIFFERS_FROM_DOC, $func->get_line()); } $fp->set_mtype($mtype); $fp->set_has_doc(true); } else { $this->report_error('Found PHPDoc for parameter "' . $param . '" (' . $match . '),' . ' but the parameter does not exist', PC_Obj_Error::E_T_DOC_WITHOUT_PARAM, $func->get_line()); } } // look for throws preg_match_all('/\\@throws\\s+([^\\s]+)/', $doc, $matches); foreach ($matches[1] as $k => $match) { $func->add_throw($match, PC_Obj_Method::THROW_SELF); } // look for return-type if (preg_match('/\\@return\\s+&?\\s*([^\\s]+)/', $doc, $matches)) { $mtype = PC_Obj_MultiType::get_type_by_name($matches[1]); if ($mtype !== null) { $rettype = $func->get_return_type(); if ($rettype && !$rettype->is_unknown() && !$rettype->equals($mtype)) { $this->report_error('PHPDoc (' . $mtype . ') does not match the return type (' . $rettype . ')', PC_Obj_Error::E_T_RETURN_DIFFERS_FROM_DOC, $func->get_line()); } if (!$rettype || $rettype->is_unknown()) { $func->set_return_type($mtype); } $func->set_has_return_doc(true); } } unset($this->funcComments[$func->get_name()]); } }
/** * Determines the multi-type from given type-name and default-value * * @param string $type the type-name * @param string $default the default-value * @return PC_Obj_MultiType the multitype */ private static function get_param_type($type, $default = null) { $type = trim($type); // "callback" is a pseudo-type that may be an array (with classname and funcname) or // a string (the funcname) if (strcasecmp($type, 'callback') == 0) { return new PC_Obj_MultiType(array(new PC_Obj_Type(PC_Obj_Type::TARRAY), new PC_Obj_Type(PC_Obj_Type::STRING))); } $otype = PC_Obj_MultiType::get_type_by_name(trim($type)); if (!$otype->is_unknown() && !$otype->is_multiple() && $default !== null && strcasecmp($default, 'null') != 0) { if ($default == 'array()') { $otype->get_first()->set_value(array()); } else { $otype->get_first()->set_value($default); } } return $otype; }