Пример #1
0
 /**
  * Analyzes the return-types of the current function and reports errors, if necessary
  *
  * @param PC_Engine_Scope $scope the current scope
  */
 public function analyze($scope)
 {
     $funcname = $scope->get_name_of(T_FUNC_C);
     $classname = $scope->get_name_of(T_CLASS_C);
     $func = $this->env->get_types()->get_method_or_func($classname, $funcname);
     if ($func && !$func->is_abstract()) {
         $hasnull = false;
         $hasother = false;
         foreach ($this->allrettypes as $t) {
             if ($t === null) {
                 $hasnull = true;
             } else {
                 $hasother = true;
             }
         }
         if (count($this->allrettypes) == 0) {
             $mtype = PC_Obj_MultiType::create_void();
         } else {
             $mtype = new PC_Obj_MultiType();
             foreach ($this->allrettypes as $t) {
                 if ($t !== null) {
                     $mtype->merge($t, false);
                 } else {
                     $mtype->merge(PC_Obj_MultiType::create_void(), false);
                 }
             }
         }
         if ($func->is_constructor()) {
             if ($hasother) {
                 $this->report($func, 'The constructor of "' . $classname . '" has a return-statement with expression', PC_Obj_Error::E_S_CONSTR_RETURN);
             }
         } else {
             $name = ($classname ? '#' . $classname . '#::' : '') . $funcname;
             // empty return-expression and non-empty?
             if ($hasnull && $hasother) {
                 $this->report($func, 'The function/method "' . $name . '" has return-' . 'statements without expression and return-statements with expression', PC_Obj_Error::E_S_MIXED_RET_AND_NO_RET);
             }
             $void = new PC_Obj_Type(PC_Obj_Type::VOID);
             $docreturn = $func->has_return_doc() && !$func->get_return_type()->contains($void);
             if ($docreturn && !$hasother) {
                 $this->report($func, 'The function/method "' . $name . '" has a return-specification in PHPDoc' . ', but does not return a value', PC_Obj_Error::E_S_RET_SPEC_BUT_NO_RET);
             } else {
                 if (!$docreturn && !$func->is_anonymous() && $hasother) {
                     $this->report($func, 'The function/method "' . $name . '" has no return-specification in PHPDoc' . ', but does return a value', PC_Obj_Error::E_S_RET_BUT_NO_RET_SPEC);
                 } else {
                     if ($this->has_forbidden($this->allrettypes, $func->get_return_type())) {
                         $this->report($func, 'The return-specification (PHPDoc) of function/method "' . $name . '" does not match with ' . 'the returned values (spec="' . $func->get_return_type() . '", returns="' . $mtype . '")', PC_Obj_Error::E_S_RETURNS_DIFFER_FROM_SPEC);
                     }
                 }
             }
         }
         if ($func->get_return_type()->is_unknown()) {
             $func->set_return_type($mtype);
         }
     }
     $this->allrettypes = array();
 }
Пример #2
0
    public function test__get()
    {
        // public mixed __get ( string $name )
        $code = '<?php
class A {
	public function __get($name);
}
class B {
	/**
	 * @return int
	 */
	public function __get($name);
}
?>';
        list(, $classes, , , $errors) = $this->analyze($code);
        $params = array(new PC_Obj_Parameter('name', PC_Obj_MultiType::create_string()));
        self::assert_equals(2, count($errors));
        $m = $classes['a']->get_method('__get');
        self::assertParamsEqual($params, $m->get_params());
        self::assert_equals((string) PC_Obj_MultiType::create_void(), (string) $m->get_return_type());
        $m = $classes['b']->get_method('__get');
        self::assertParamsEqual($params, $m->get_params());
        self::assert_equals((string) PC_Obj_MultiType::create_int(), (string) $m->get_return_type());
        $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#::__get" 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_S_RET_SPEC_BUT_NO_RET, $error->get_type());
        self::assert_regex('/The function\\/method "#B#::__get" has a return-specification in PHPDoc, but does not return a value/', $error->get_msg());
    }
Пример #3
0
    public function test_funcs()
    {
        $code = '<?php
function a() {}
/**
 * @param string $a
 */
function b($a) {}

class myc2 extends myc {
	public static function mystatic() {}
	public function doit() {
		parent::doit();
		self::mystatic();
		$this->c(array(),2);
	}
	/**
	 * @param array $a
	 * @param int $b
	 */
	protected function c($a,$b = 0) {}
	/**
	 * @param int $a
	 * @param string $b
	 * @param boolean $c
	 * @return int
	 */
	private function d($a = 0,$b = "a",$c = false) {
		$a = $b + $c;
		return $a;
	}
	/**
	 * @param MyClass $c
	 * @param int $d
	 */
	public function doit(MyClass $c,$d) {
		$c->test($d);
	}
}
abstract class myc {
	public abstract function doit();
}
?>';
        list($functions, $classes, , $calls, $errors) = $this->analyze($code);
        self::assert_equals(0, count($errors));
        $func = $functions['a'];
        /* @var $func PC_Obj_Method */
        self::assert_equals('a', $func->get_name());
        self::assert_equals(0, $func->get_param_count());
        self::assert_equals(0, $func->get_required_param_count());
        self::assert_equals((string) PC_Obj_MultiType::create_void(), (string) $func->get_return_type());
        $func = $functions['b'];
        self::assert_equals('b', $func->get_name());
        self::assert_equals(1, $func->get_param_count());
        self::assert_equals(1, $func->get_required_param_count());
        self::assert_equals((string) PC_Obj_MultiType::create_void(), (string) $func->get_return_type());
        self::assert_equals('string', (string) $func->get_param('a'));
        $class = $classes['myc2'];
        $func = $class->get_method('c');
        self::assert_equals('c', $func->get_name());
        self::assert_equals(2, $func->get_param_count());
        self::assert_equals(1, $func->get_required_param_count());
        self::assert_equals((string) PC_Obj_MultiType::create_void(), (string) $func->get_return_type());
        self::assert_equals('array', (string) $func->get_param('a'));
        self::assert_equals('integer?', (string) $func->get_param('b'));
        $func = $class->get_method('d');
        self::assert_equals('d', $func->get_name());
        self::assert_equals(3, $func->get_param_count());
        self::assert_equals(0, $func->get_required_param_count());
        self::assert_equals((string) PC_Obj_MultiType::create_int(), (string) $func->get_return_type());
        self::assert_equals('integer?', (string) $func->get_param('a'));
        self::assert_equals('string?', (string) $func->get_param('b'));
        self::assert_equals('bool?', (string) $func->get_param('c'));
        $func = $class->get_method('doit');
        self::assert_equals('doit', $func->get_name());
        self::assert_equals(2, $func->get_param_count());
        self::assert_equals(2, $func->get_required_param_count());
        self::assert_equals((string) PC_Obj_MultiType::create_void(), (string) $func->get_return_type());
        self::assert_equals('MyClass', (string) $func->get_param('c'));
        self::assert_equals('integer', (string) $func->get_param('d'));
        self::assert_equals('myc->doit()', (string) $calls[0]);
        self::assert_equals('myc2::mystatic()', (string) $calls[1]);
    }
Пример #4
0
 /**
  * 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;
 }