public function __ToString() { $str = $this->visibility . ' ' . ($this->static ? 'static ' : '') . $this->get_name(); if (!$this->type->is_unknown()) { $str .= '[' . $this->type . ']'; } return $str; }
/** * Determines the type from the operation (assumes that the type or the value is unknown) * * @param string $op the operator * @param PC_Obj_MultiType $t1 the type of the first operand * @param PC_Obj_MultiType $t2 the type of the second operand (may be null for unary ops) * @return PC_Obj_MultiType the variable */ protected function get_type_from_op($op, $t1, $t2 = null) { switch ($op) { // bitwise operators have always int as result case '|': case '&': case '^': case '>>': case '<<': case '~': case '?:': return PC_Obj_MultiType::create_int(); // concatenation leads always to string // concatenation leads always to string case '.': return PC_Obj_MultiType::create_string(); case '+': case '-': case '*': case '/': case '%': // if one of them is unknown we don't know whether we would get a float or int if ($t1->is_unknown() || $t1->is_multiple() || $t2 !== null && ($t2->is_unknown() || $t2->is_multiple())) { return new PC_Obj_MultiType(); } $ti1 = $t1->get_first()->get_type(); $ti2 = $t2 === null ? -1 : $t2->get_first()->get_type(); // if both are arrays, the result is an array if ($ti1 == PC_Obj_Type::TARRAY && $ti2 == PC_Obj_Type::TARRAY) { return PC_Obj_MultiType::create_array(); } // if one of them is float, the result is float if ($ti1 == PC_Obj_Type::FLOAT || $ti2 == PC_Obj_Type::FLOAT) { return PC_Obj_MultiType::create_float(); } // otherwise its always int return PC_Obj_MultiType::create_int(); case '==': case '!=': case '===': case '!==': case '<': case '>': case '<=': case '>=': case '&&': case '||': case 'xor': case '!': // always bool return PC_Obj_MultiType::create_bool(); default: FWS_Helper::error('Unknown operator "' . $op . '"'); } }
/** * Adds the given expression as thrown * * @param PC_Obj_MultiType $expr the expression */ public function add_throw($expr) { if (!$expr instanceof PC_Obj_MultiType) { $this->handle_error('$expr is invalid'); return; } // don't even collect unknown types here if (!$expr->is_unknown()) { $this->throws_analyzer->add(PC_Obj_Method::THROW_SELF, $expr); } }
/** * Checks whether $arg is ok for $param * * @param PC_Obj_Location $loc the location * @param PC_Obj_MultiType $arg the argument * @param PC_Obj_Parameter $param the parameter * @return boolean true if so */ private function is_argument_ok($loc, $arg, $param) { // not present but required? if ($arg === null && !$param->is_optional() && !$param->is_first_vararg()) { return false; } // unknown / not present if ($arg === null) { return true; } // callables are special if ($param->get_mtype()->get_first()->get_type() == PC_Obj_Type::TCALLABLE) { if (!$arg->is_unknown()) { $this->check_callable($loc, $arg); return true; } } // arg in the allowed types? return $this->env->get_types()->is_type_conforming($arg, $param->get_mtype()); }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; } } }