public function __ToString() { $str = $this->visibility . ' ' . ($this->static ? 'static ' : '') . $this->get_name(); if (!$this->type->is_unknown()) { $str .= '[' . $this->type . ']'; } return $str; }
function yy_r446() { $this->_retvalue = PC_Obj_MultiType::create_array(array()); $this->_retvalue->get_first()->set_array_type($this->yystack[$this->yyidx + 0]->minor['key'], $this->yystack[$this->yyidx + 0]->minor['val'], $this->yystack[$this->yyidx + 0]->minor['append']); }
/** * Determines the article for the given type ('a' or 'an'), depending on the first char * * @param PC_Obj_MultiType $type the type * @return string the article */ private function get_article($type) { $str = $type === null ? 'x' : $type->__ToString(); return isset($str[0]) && in_array(strtolower($str[0]), array('i', 'a', 'o', 'u', 'e')) ? 'an' : 'a'; }
public function test_special() { $code = '<?php $u = `foo`; // we can even say the type/value of variable variables if we can figure out the variable name ^^ $v = "u"; $w = ${$v}; $x = ${"unknown var"}; $y = ${123}; // array-union works when all values are known $z = array("a" => 1,"c" => 123,"d" => 5) + array("a" => 2,"b" => 4); $aa = array(array($_,1)) + array(2); // comparison is just bool $ab = array(1) === array(1); $ac = array(1,"2") == array(1,2); $ad = array(1) <> array($_); $ba = 1 ? 0 : 2; $bb = 4 ? "bla" : "blub"; $bc = $_ ? 1 : 2; $bd = $_ ? "foo" : 1; $be = (1) ? ((2) ? "a" : "b") : "c"; $bf = 1 ?: -1; $bg = "f" ?: "g"; ?>'; list(, , $vars, , $errors) = $this->analyze($code); self::assert_equals(0, count($errors)); $global = $vars[PC_Obj_Variable::SCOPE_GLOBAL]; self::assert_equals((string) PC_Obj_MultiType::create_string(), (string) $global['u']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_string(), (string) $global['w']->get_type()); self::assert_equals((string) new PC_Obj_MultiType(), (string) $global['x']->get_type()); self::assert_equals((string) new PC_Obj_MultiType(), (string) $global['y']->get_type()); self::assert_equals((string) PC_Obj_Type::get_type_by_value(array("a" => 1, "c" => 123, "d" => 5, "b" => 4)), (string) $global['z']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_array(), (string) $global['aa']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_bool(), (string) $global['ab']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_bool(), (string) $global['ac']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_bool(), (string) $global['ad']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_int(0), (string) $global['ba']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_string("bla"), (string) $global['bb']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_int(), (string) $global['bc']->get_type()); self::assert_equals((string) new PC_Obj_MultiType(array(new PC_Obj_Type(PC_Obj_Type::STRING, "foo"), new PC_Obj_Type(PC_Obj_Type::INT, 1))), (string) $global['bd']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_string("a"), (string) $global['be']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_int(1), (string) $global['bf']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_int(-1), (string) $global['bg']->get_type()); }
/** * 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; }
public function test_arrays() { $code = '<?php /** @param mixed $a */ function func($a) {} class a { /** @param int $x */ function a($x) {} } $x = array(); $x[] = 4; $x[] = 5; $y = $x; $x = array(); $z = clone $y; $z[] = 6; $c = array(1,2,3,array(array(\'abc\',2))); func($c[0]); func($c[1]); func($c[2]); func($c[3][0]); func($c[3][0][0]); func($c[3][0][1]); func($c[4]); func($c[3][0][1][0]); $a = array(); $a[] = new a(1); $a[] = 4; $a[] = 5; $a["Abc"] = "me"; $d = array(0,array(1),2,3); $d[1][0] = 2; $e = array(); $e{1} = 4; $e{"foo"} = 5; class foo { public function bar() { $a = array( 0 => 4, self::R_TYPESCANNER => array(), ); } } /** @return array */ function f() { return $_; } $g = f(); $g[] = 1; ?>'; list(, , $vars, $calls, $errors) = $this->analyze($code); self::assert_equals(0, count($errors)); $args = $calls[0]->get_arguments(); self::assert_equals((string) PC_Obj_MultiType::create_int(1), (string) $args[0]); $args = $calls[1]->get_arguments(); self::assert_equals((string) PC_Obj_MultiType::create_int(2), (string) $args[0]); $args = $calls[2]->get_arguments(); self::assert_equals((string) PC_Obj_MultiType::create_int(3), (string) $args[0]); $args = $calls[3]->get_arguments(); $type = PC_Obj_MultiType::create_array(array()); $type->get_first()->set_array_type(0, PC_Obj_MultiType::create_string('abc')); $type->get_first()->set_array_type(1, PC_Obj_MultiType::create_int(2)); self::assert_equals((string) $type, (string) $args[0]); $args = $calls[4]->get_arguments(); self::assert_equals((string) PC_Obj_MultiType::create_string('abc'), (string) $args[0]); $args = $calls[5]->get_arguments(); self::assert_equals((string) PC_Obj_MultiType::create_int(2), (string) $args[0]); $args = $calls[6]->get_arguments(); self::assert_equals((string) new PC_Obj_MultiType(), (string) $args[0]); $args = $calls[7]->get_arguments(); self::assert_equals((string) new PC_Obj_MultiType(), (string) $args[0]); $global = $vars[PC_Obj_Variable::SCOPE_GLOBAL]; self::assert_equals((string) PC_Obj_Type::get_type_by_value(array()), (string) $global['x']->get_type()); self::assert_equals((string) PC_Obj_Type::get_type_by_value(array(4, 5)), (string) $global['y']->get_type()); self::assert_equals((string) PC_Obj_Type::get_type_by_value(array(4, 5, 6)), (string) $global['z']->get_type()); $type = PC_Obj_MultiType::create_array(array()); $type->get_first()->set_array_type(0, PC_Obj_MultiType::create_object('a')); $type->get_first()->set_array_type(1, PC_Obj_MultiType::create_int(4)); $type->get_first()->set_array_type(2, PC_Obj_MultiType::create_int(5)); $type->get_first()->set_array_type('Abc', PC_Obj_MultiType::create_string('me')); self::assert_equals((string) $type, (string) $global['a']->get_type()); $type = PC_Obj_MultiType::create_array(array()); $type->get_first()->set_array_type(0, PC_Obj_MultiType::create_int(0)); $subtype = PC_Obj_MultiType::create_array(array()); $subtype->get_first()->set_array_type(0, PC_Obj_MultiType::create_int(2)); $type->get_first()->set_array_type(1, $subtype); $type->get_first()->set_array_type(2, PC_Obj_MultiType::create_int(2)); $type->get_first()->set_array_type(3, PC_Obj_MultiType::create_int(3)); self::assert_equals((string) $type, (string) $global['d']->get_type()); $bar = $vars['foo::bar']; self::assert_equals('array', (string) $bar['a']->get_type()); $type = PC_Obj_MultiType::create_array(array()); $type->get_first()->set_array_type(1, PC_Obj_MultiType::create_int(4)); $type->get_first()->set_array_type("foo", PC_Obj_MultiType::create_int(5)); self::assert_equals((string) $type, (string) $global['e']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_array(), (string) $global['g']->get_type()); }
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; }
/** * Checks if the given method is "magic". If so it adds automatically parameter-types (if not * already set). * * @param string $classname the class-name * @param PC_Obj_Method $method the method * @return bool true if handled */ private function handle_magic($classname, $method) { // the magic-methods: // public void __set ( string $name , mixed $value ) // public mixed __get ( string $name ) // public bool __isset ( string $name ) // public void __unset ( string $name ) // public mixed __call ( string $name , array $arguments ) // public mixed __callStatic ( string $name , array $arguments ) // * array __sleep( void ) // * void__wakeup( void ) // public string __toString( void ) // public void __invoke( ... ) // public static object __set_state( array $props ) // * void __clone( void ) $ismagic = true; $visibility = PC_Obj_Visible::V_PUBLIC; $static = false; $expected = null; switch (strtolower($method->get_name())) { case '__set': $expected = array(new PC_Obj_Parameter('name', PC_Obj_MultiType::create_string()), new PC_Obj_Parameter('value', new PC_Obj_MultiType())); break; case '__isset': case '__unset': case '__get': $expected = array(new PC_Obj_Parameter('name', PC_Obj_MultiType::create_string())); break; case '__call': case '__callstatic': $expected = array(new PC_Obj_Parameter('name', PC_Obj_MultiType::create_string()), new PC_Obj_Parameter('arguments', PC_Obj_MultiType::create_array())); break; case '__tostring': $expected = array(); break; case '__sleep': case '__wakeup': case '__clone': $visibility = null; // may be private or protected $expected = array(); break; case '__invoke': $static = true; break; case '__set_state': $expected = array(new PC_Obj_Parameter('props', PC_Obj_MultiType::create_array())); break; default: $ismagic = false; break; } if (!$ismagic) { return false; } if ($visibility !== null && $method->get_visibility() != $visibility) { $this->report($method, 'The magic method "#' . $classname . '#::' . $method->get_name() . '" should be public', PC_Obj_Error::E_T_MAGIC_NOT_PUBLIC); } if ($static && !$method->is_static()) { $this->report($method, 'The magic method "#' . $classname . '#::' . $method->get_name() . '" should be static', PC_Obj_Error::E_T_MAGIC_NOT_STATIC); } else { if (!$static && $method->is_static()) { $this->report($method, 'The magic method "#' . $classname . '#::' . $method->get_name() . '" should not be static', PC_Obj_Error::E_T_MAGIC_IS_STATIC); } } if ($expected !== null) { $changed = false; if ($this->check_params($classname, $method, $expected, $method->get_params())) { // set parameter-descriptions $i = 0; foreach ($method->get_params() as $param) { $param->set_mtype($expected[$i++]->get_mtype()); $param->set_has_doc(true); $changed = true; } } $return = new PC_Obj_MultiType(); switch (strtolower($method->get_name())) { case '__set': case '__unset': case '__wakeup': case '__clone': $return = PC_Obj_MultiType::create_void(); break; case '__set_state': $return = PC_Obj_MultiType::create_object(); break; case '__isset': $return = PC_Obj_MultiType::create_bool(); break; case '__sleep': $return = PC_Obj_MultiType::create_array(); break; case '__tostring': $return = PC_Obj_MultiType::create_string(); break; } if ($method->has_return_doc() && !$this->env->get_types()->is_type_conforming($method->get_return_type(), $return)) { // its ok to specify a more specific return-value if the expected one is "mixed" if (!$return->is_unknown()) { $this->report($method, 'The return-type of the magic-method "#' . $classname . '#::' . $method->get_name() . '" is invalid ' . '(expected="' . $return . '", found="' . $method->get_return_type() . '")', PC_Obj_Error::E_T_MAGIC_METHOD_RET_INVALID); } } if ($return !== null && !$method->has_return_doc()) { $method->set_return_type($return); $method->set_has_return_doc(true); $changed = true; } if ($changed) { $this->env->get_storage()->update_function($method, $method->get_class()); } } return true; }
/** * Determines the next array-key to use * * @return PC_Obj_MultiType the key */ public function get_next_array_key() { if ($this->_type != self::TARRAY || $this->_value === null) { return PC_Obj_MultiType::create_int(0); } $max = -1; foreach (array_keys($this->_value) as $k) { if (FWS_Helper::is_integer($k) && $k > $max) { $max = $k; } } return PC_Obj_MultiType::create_int($max + 1); }
/** * Returns a variable pointing to the array-element with given key. If it does not exist, it * will be created as soon as the type is assigned. * * @param PC_Obj_MultiType $key the key (null = append) * @return PC_Obj_Variable the type of the element */ public function array_offset($key) { assert(!$this->type->is_multiple() && !$this->type->is_unknown()); $first = $this->type->get_first(); assert($first->get_type() == PC_Obj_Type::TARRAY); // fetch element or create it $akey = $key; if ($key === null) { $akey = $key = $first->get_next_array_key(); } else { if (!$key->is_unknown()) { $akey = $key; $key = $first->get_array_type($key->get_first()->get_value()); } } $var = new self($this->get_file(), $this->get_line(), '', $key); // connect the var to us $var->arrayref = $this; $var->arrayoff = $akey; return $var; }
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); }
public function test_vars() { $code = '<?php define("MYCONST",123); $i1 = +1; $i2 = -412; $i3 = MYCONST; $i4 = (int)"abc"; $f1 = 0.5; $f2 = 0.123; $f3 = 1.0; $f4 = (float)(string)2; $s1="my\'str"; $s2 = \'str2\'; $s3 = "ab $b c\'a\\\\\\""."bla"; $s4 = "ab c\'a\\\\\\""."bla"; $b1 = true; $b2 = false; $a1 = array(); $a2 = array(1); $a3 = ARRAY(1,2,3); $a4 = array(1 => 2,3 => 4,5 => 6); $a5 = array(\'a\' => 1,2,3,\'4\'); $a6 = array(array(array(1,2,3),4),5); $a7 = (array)1; $a8 = 4; unset($a8); $a9 = "foo"; $a10 = 123; unset($a9,$a10); /** * @param array $a * @param MyClass $b * @return int */ function x($a,MyClass $b) { global $b1; $i1 = $a; $i2 = $i1; return 1; } ?>'; $options = new PC_Engine_Options(); $options->add_project(PC_Project::PHPREF_ID); $options->add_min_req('PHP', '5'); list(, , $vars, , $errors) = $this->analyze($code, $options); self::assert_equals(0, count($errors)); $global = $vars[PC_Obj_Variable::SCOPE_GLOBAL]; self::assert_equals((string) PC_Obj_MultiType::create_int(1), (string) $global['i1']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_int(-412), (string) $global['i2']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_int(123), (string) $global['i3']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_int(0), (string) $global['i4']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_float(0.5), (string) $global['f1']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_float(0.123), (string) $global['f2']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_float(1.0), (string) $global['f3']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_float(2.0), (string) $global['f4']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_string('my\'str'), (string) $global['s1']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_string('str2'), (string) $global['s2']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_string(), (string) $global['s3']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_string('ab c\'a\\\\\\"bla'), (string) $global['s4']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_bool(true), (string) $global['b1']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_bool(false), (string) $global['b2']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_array(array()), (string) $global['a1']->get_type()); $array = new PC_Obj_MultiType(PC_Obj_Type::get_type_by_value(array(1))); self::assert_equals((string) $array, (string) $global['a2']->get_type()); $array = new PC_Obj_MultiType(PC_Obj_Type::get_type_by_value(array(1, 2, 3))); self::assert_equals((string) $array, (string) $global['a3']->get_type()); $array = new PC_Obj_MultiType(PC_Obj_Type::get_type_by_value(array(1 => 2, 3 => 4, 5 => 6))); self::assert_equals((string) $array, (string) $global['a4']->get_type()); $array = new PC_Obj_MultiType(PC_Obj_Type::get_type_by_value(array('a' => 1, 2, 3, '4'))); self::assert_equals((string) $array, (string) $global['a5']->get_type()); $array = new PC_Obj_MultiType(PC_Obj_Type::get_type_by_value(array(array(array(1, 2, 3), 4), 5))); self::assert_equals((string) $array, (string) $global['a6']->get_type()); $array = new PC_Obj_MultiType(PC_Obj_Type::get_type_by_value((array) 1)); self::assert_equals((string) $array, (string) $global['a7']->get_type()); self::assert_equals(false, isset($global['a8'])); self::assert_equals(false, isset($global['a9'])); self::assert_equals(false, isset($global['a10'])); $x = $vars['x']; self::assert_equals((string) PC_Obj_MultiType::create_array(), (string) $x['a']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_object('MyClass'), (string) $x['b']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_array(), (string) $x['i1']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_array(), (string) $x['i2']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_bool(true), (string) $x['b1']->get_type()); }
/** * Merges all types in the given multitype into this one. Clones the types in it. * * @param PC_Obj_MultiType $mtype the type to merge with * @param bool $set_unknown whether to set the types to unknown when $this or $mtype is unknown */ public function merge($mtype, $set_unknown = true) { // if one is unknown, the merged type is unknown as well if ($set_unknown && ($this->is_unknown() || $mtype->is_unknown())) { $this->types = array(); return; } if (!isset($mtype->types)) { return; } foreach ($mtype->types as $type) { $found = false; foreach ($this->types as $ttype) { if ($ttype->equals($type)) { $found = true; break; } } // if we already have this type, check if the values are different if ($found) { // if so simply unset the value, because we don't know it anymore if ($ttype->get_value() !== $type->get_value()) { $ttype->set_value(null); } } else { $this->types[] = clone $type; } } }
function yy_r473() { $this->_retvalue = PC_Obj_MultiType::create_bool(); }
public function test_nesting() { $code = '<?php /** @param int $a */ function f1(int $a) {} /** @param int $a */ function f2(int $a) {} /** @param int $a */ function f3(int $a) {} class A { function a() { $a = 1; function b() { $b = 2; function c() { $c = 3; function d() { $d = 4; } f3($c); } f2($b); } f1($a); } } function e() { $e = 5; function f() { $f = 6; class B { function g() { static $h = 4; $g = 7; } } } } ?>'; list(, , $vars, $calls, $errors) = $this->analyze($code); self::assert_equals(0, count($errors)); self::assert_equals('f3(integer=3)', (string) $calls[0]); self::assert_equals('f2(integer=2)', (string) $calls[1]); self::assert_equals('f1(integer=1)', (string) $calls[2]); self::assert_equals((string) PC_Obj_MultiType::create_int(1), (string) $vars['A::a']['a']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_int(2), (string) $vars['b']['b']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_int(3), (string) $vars['c']['c']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_int(4), (string) $vars['d']['d']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_int(5), (string) $vars['e']['e']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_int(6), (string) $vars['f']['f']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_int(7), (string) $vars['B::g']['g']->get_type()); self::assert_equals((string) PC_Obj_MultiType::create_int(4), (string) $vars['B::g']['h']->get_type()); }
public function test_nesting() { $code = '<?php /** @param mixed $a */ function func($a) {} $a = 0; while(true) { if($_) $a++; else $a--; } // now we know that its still an integer, but dont know the value foreach($_ as $_) { while(true) { do { $b = 4; } while(true); } } // $b is unknown since we didnt know it before $c = "str"; foreach($_ as $_) { while(true) { do { $c = 1; } while(true); } } // $c is an int or string since we knew it before $d = 1; if($_) { if($_) { if($_) { if($_) { $d = "str"; } } } } // $d is an int or string since we knew it before $e = 1; if($_) { $e = 2; if($_) { $e = 12.3; if($_) { $e = "str"; if($_) { $e = true; func($e); } func($e); } func($e); } func($e); } func($e); // $e is an int, string, float or bool. note that we even know the value except for the int if($_) { $f = 0; if($_) { if($_) $f = 2; else $f = 4; // here we know that f is an int func($f); } // here we still know that because $f was assigned in this block before func($f); } // here we dont know that anymore since it didnt exist before ?>'; list(, , $vars, $calls, $errors) = $this->analyze($code); self::assert_equals(0, count($errors)); $global = $vars[PC_Obj_Variable::SCOPE_GLOBAL]; self::assert_equals((string) PC_Obj_MultiType::create_int(), (string) $global['a']->get_type()); self::assert_equals((string) new PC_Obj_MultiType(), (string) $global['b']->get_type()); $type = new PC_Obj_MultiType(array(new PC_Obj_Type(PC_Obj_Type::INT, 1), new PC_Obj_Type(PC_Obj_Type::STRING, "str"))); self::assert_equals((string) $type, (string) $global['c']->get_type()); $type = new PC_Obj_MultiType(array(new PC_Obj_Type(PC_Obj_Type::STRING, "str"), new PC_Obj_Type(PC_Obj_Type::INT, 1))); self::assert_equals((string) $type, (string) $global['d']->get_type()); $type = new PC_Obj_MultiType(array(new PC_Obj_Type(PC_Obj_Type::BOOL, true), new PC_Obj_Type(PC_Obj_Type::STRING, "str"), new PC_Obj_Type(PC_Obj_Type::FLOAT, 12.3), new PC_Obj_Type(PC_Obj_Type::INT))); self::assert_equals((string) $type, (string) $global['e']->get_type()); self::assert_equals((string) new PC_Obj_MultiType(), (string) $global['f']->get_type()); self::assert_equals('func(bool=1)', (string) $calls[0]); self::assert_equals('func(bool=1 or string=str)', (string) $calls[1]); self::assert_equals('func(bool=1 or string=str or float=12.3)', (string) $calls[2]); self::assert_equals('func(bool=1 or string=str or float=12.3 or integer=2)', (string) $calls[3]); self::assert_equals('func(bool=1 or string=str or float=12.3 or integer)', (string) $calls[4]); self::assert_equals('func(integer)', (string) $calls[5]); self::assert_equals('func(integer)', (string) $calls[6]); }
public function test__set_state() { // public static object __set_state( array $props ) $code = '<?php class A { public function __set_state($arr); } class B { public static function __set_state($arr); } ?>'; list(, $classes, , , $errors) = $this->analyze($code); $params = array(new PC_Obj_Parameter('props', PC_Obj_MultiType::create_array())); $m = $classes['a']->get_method('__set_state'); self::assertParamsEqual($params, $m->get_params()); self::assert_equals((string) PC_Obj_MultiType::create_object(), (string) $m->get_return_type()); $m = $classes['b']->get_method('__set_state'); self::assertParamsEqual($params, $m->get_params()); self::assert_equals((string) PC_Obj_MultiType::create_object(), (string) $m->get_return_type()); self::assert_equals(3, count($errors)); $error = $errors[0]; self::assert_equals(PC_Obj_Error::E_S_RET_SPEC_BUT_NO_RET, $error->get_type()); self::assert_regex('/The function\\/method "#A#::__set_state" has a return-specification in PHPDoc, but does not return a value/', $error->get_msg()); $error = $errors[1]; self::assert_equals(PC_Obj_Error::E_T_MAGIC_IS_STATIC, $error->get_type()); self::assert_regex('/The magic method "#B#::__set_state" should not be static/', $error->get_msg()); $error = $errors[2]; self::assert_equals(PC_Obj_Error::E_S_RET_SPEC_BUT_NO_RET, $error->get_type()); self::assert_regex('/The function\\/method "#B#::__set_state" has a return-specification in PHPDoc, but does not return a value/', $error->get_msg()); }
/** * Checks whether $actual is okay for $spec. * * @param PC_Obj_MultiType $actual the actual type * @param PC_Obj_MultiType $spec the specified type, i.e., the one to check against * @return bool true if ok */ public function is_type_conforming($actual, $spec) { if ($actual->is_unknown() || $spec->is_unknown()) { return true; } // every actual type has to be contained in at least one of the specified types $count = 0; foreach ($actual->get_types() as $atype) { $ok = false; foreach ($spec->get_types() as $stype) { if ($atype->equals($stype)) { $ok = true; break; } // floats can accept ints if ($atype->get_type() == PC_Obj_Type::INT && $stype->get_type() == PC_Obj_Type::FLOAT) { $ok = true; break; } // if both are objects, check if the actual is the same or a subclass of the spec $objs = $atype->get_type() == PC_Obj_Type::OBJECT && $stype->get_type() == PC_Obj_Type::OBJECT; if ($objs && ($stype->get_class() == '' || (strcasecmp($atype->get_class(), $stype->get_class()) || $this->is_subclass_of($atype->get_class(), $stype->get_class())))) { $ok = true; break; } } // early exit? if (!$ok && $this->options->get_report_argret_strictly()) { return false; } if ($ok && !$this->options->get_report_argret_strictly()) { return true; } if ($ok) { $count++; } } return $count > 0; }
/** * Determines the type for the given type name, which is used, e.g., for return specifications. * * @param string $name the name * @return PC_Obj_MultiType the type */ public function get_type_by_name($name) { switch ($name) { case 'array': return PC_Obj_MultiType::create_array(); case 'callable': return PC_Obj_MultiType::create_callable(); case 'bool': return PC_Obj_MultiType::create_bool(); case 'float': return PC_Obj_MultiType::create_float(); case 'int': return PC_Obj_MultiType::create_int(); case 'string': return PC_Obj_MultiType::create_string(); case 'self': // TODO get class name return PC_Obj_MultiType::create_object(); default: return PC_Obj_MultiType::create_object($name); } }
/** * Checks whether $types contains a type, that is not contained in $mtype. * * @param array $types the types * @param PC_Obj_MultiType $mtype the multitype * @return bool true if so */ private function has_forbidden($types, $mtype) { // if the type is unknown (mixed), its always ok if ($mtype->is_unknown()) { return false; } foreach ($types as $t) { if ($t !== null && !$this->env->get_types()->is_type_conforming($t, $mtype)) { return true; } } return false; }