/**
  * Finishes the classes. That means, inheritance will be performed and missing
  * constructors will be added.
  */
 public function finalize()
 {
     foreach ($this->env->get_types()->get_classes() as $c) {
         if ($c->get_pid() != $this->env->get_options()->get_current_project()) {
             continue;
         }
         /* @var $c PC_Obj_Class */
         $this->add_members($c, $c->get_name());
         // add missing constructor
         if (!$c->is_interface() && $c->get_method('__construct') === null) {
             $method = new PC_Obj_Method($c->get_file(), -1, false);
             $method->set_name('__construct');
             $method->set_visibility(PC_Obj_Visible::V_PUBLIC);
             $c->add_method($method);
             $this->env->get_storage()->create_function($method, $c->get_id());
         }
     }
 }
Example #2
0
 /**
  * Determines the type of the given function-parameter
  * 
  * @param PC_Obj_Method $func the function/method
  * @param string $varname the variable-name
  * @return PC_Obj_MultiType the type
  */
 private function get_funcparam_type($func, $varname)
 {
     if ($func === null) {
         return null;
     }
     $param = $func->get_param($varname);
     if ($param === null) {
         return null;
     }
     return $param->get_mtype();
 }
Example #3
0
 /**
  * Checks the parameters of the call against the given ones
  *
  * @param PC_Obj_Call $call the call
  * @param PC_Obj_Method $method the method
  */
 private function check_params($call, $method)
 {
     $arguments = $call->get_arguments();
     $nparams = $method->get_required_param_count();
     $nmaxparams = $method->get_param_count();
     if (count($arguments) < $nparams || $nmaxparams >= 0 && count($arguments) > $nmaxparams) {
         if ($nparams != $nmaxparams) {
             $reqparams = $nparams . ' to ' . ($nmaxparams == -1 ? '*' : $nmaxparams);
         } else {
             $reqparams = $nparams;
         }
         $this->report($call, 'The function/method called by "' . $this->get_call_link($call) . '" requires ' . $reqparams . ' arguments but you have given ' . count($arguments), PC_Obj_Error::E_S_WRONG_ARGUMENT_COUNT);
     } else {
         $i = 0;
         foreach ($method->get_params() as $param) {
             /* @var $param PC_Obj_Parameter */
             $arg = isset($arguments[$i]) ? $arguments[$i] : null;
             // arg- or param-type unknown?
             if ($arg === null || $arg->is_unknown() || $param->get_mtype()->is_unknown()) {
                 $i++;
                 continue;
             }
             if (!$this->is_argument_ok($call, $arg, $param)) {
                 $trequired = $param->get_mtype();
                 $tactual = $arg;
                 $this->report($call, 'The parameter ' . ($i + 1) . ' in "' . $this->get_call_link($call) . '" requires ' . $this->get_article($trequired) . ' "' . $trequired . '" but you have given ' . $this->get_article($tactual) . ' "' . ($tactual === null ? "<i>NULL</i>" : $tactual) . '"', PC_Obj_Error::E_S_WRONG_ARGUMENT_TYPE);
             }
             $i++;
         }
     }
 }
Example #4
0
 /**
  * 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()]);
     }
 }
Example #5
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;
 }
Example #6
0
 /**
  * Builds a PC_Obj_Method from the given row
  *
  * @param array $row the row from db
  * @return PC_Obj_Method the method
  */
 private function build_func($row)
 {
     $c = new PC_Obj_Method($row['file'], $row['line'], $row['class'] == 0, $row['id'], $row['class']);
     $c->set_name($row['name']);
     $c->set_visibility($row['visibility']);
     $c->set_abstract($row['abstract']);
     $c->set_static($row['static']);
     $c->set_anonymous($row['anonymous']);
     $c->set_final($row['final']);
     $c->get_version()->set(unserialize($row['min_version']), unserialize($row['max_version']));
     foreach (unserialize($row['params']) as $param) {
         $c->put_param($param);
     }
     list($hasretdoc, $rettype) = unserialize($row['return_type']);
     $throws = unserialize($row['throws']);
     if (is_array($throws)) {
         foreach ($throws as $class => $type) {
             $c->add_throw($class, $type);
         }
     }
     $c->set_has_return_doc($hasretdoc);
     $c->set_return_type($rettype);
     return $c;
 }
Example #7
0
 /**
  * Parses a method-description into a PC_Obj_Method
  * 
  * @param string $file the filename
  * @param string $desc the description
  * @return array an array of the class-name and the PC_Obj_Method
  * @throws PC_PHPRef_Exception if it failed
  */
 public static function parse_method_desc($file, $desc)
 {
     $classname = '';
     // find link to method
     if (preg_match('/<a href="(.*?)" class="methodname">/', $desc, $m)) {
         $file = dirname($file) . '/' . $m[1];
     }
     // prepare description
     $desc = trim(strip_tags($desc));
     $desc = FWS_StringHelper::htmlspecialchars_back($desc);
     // filter out modifier, return-type, name and params
     $match = array();
     $res = preg_match('/^(?:(abstract|static|final|public|protected|private)\\s*)?' . '(?:(abstract|static|final|public|protected|private)\\s*)?' . '(?:(abstract|static|final|public|protected|private)\\s*)?' . '(?:(\\S+)\\s+)?' . '([a-zA-Z0-9_:\\-\\>]+)\\s*\\((.*?)\\)$/s', $desc, $match);
     if (!$res) {
         throw new PC_PHPRef_Exception('Unable to parse "' . $desc . '"');
     }
     list(, $modifier1, $modifier2, $modifier3, $return, $name, $params) = $match;
     // detect class-names
     if (($pos = strpos($name, '::')) !== false || ($pos = strpos($name, '->')) !== false) {
         $classname = substr($name, 0, $pos);
         $name = substr($name, $pos + 2);
     }
     // build basic method
     $method = new PC_Obj_Method($file, 0, $classname != '');
     if ($modifier1 == 'static' || $modifier2 == 'static' || $modifier3 == 'static') {
         $method->set_static(true);
     }
     if ($modifier1 == 'final' || $modifier2 == 'final' || $modifier3 == 'final') {
         $method->set_final(true);
     }
     if ($modifier1 == 'abstract' || $modifier2 == 'abstract' || $modifier3 == 'abstract') {
         $method->set_abstract(true);
     }
     if (in_array($modifier1, array('private', 'protected'))) {
         $method->set_visibility($modifier1);
     } else {
         if (in_array($modifier2, array('private', 'protected'))) {
             $method->set_visibility($modifier2);
         } else {
             if (in_array($modifier3, array('private', 'protected'))) {
                 $method->set_visibility($modifier3);
             }
         }
     }
     if ($return) {
         $method->set_return_type(PC_Obj_MultiType::get_type_by_name($return));
         // set this always for builtin types since it makes no sense to report errors for
         // inherited classes or similar
         $method->set_has_return_doc(true);
     }
     $method->set_name($name);
     // check what kind of params we have
     $optional = '';
     $firstopt = strpos($params, '[');
     if ($firstopt !== false) {
         $required = substr($params, 0, $firstopt);
         $optional = substr($params, $firstopt + 1);
         $optional = str_replace(array('[', ']'), '', $optional);
     } else {
         $required = $params;
     }
     // add required ones
     $required = trim($required);
     if ($required && $required != 'void') {
         $reqparts = explode(', ', $required);
         foreach ($reqparts as $part) {
             list($type, $name) = explode(' ', trim($part));
             $param = new PC_Obj_Parameter();
             $param->set_name(trim($name));
             $param->set_mtype(self::get_param_type($type));
             $param->set_has_doc(true);
             $method->put_param($param);
         }
     }
     // add optional ones
     $optional = trim($optional);
     if ($optional) {
         $optparts = explode(', ', $optional);
         foreach ($optparts as $part) {
             $part = trim($part);
             if ($part == '') {
                 continue;
             }
             $default = null;
             $param = new PC_Obj_Parameter();
             $param->set_optional(true);
             // has it a known default-value?
             if (($pos = strpos($part, '=')) !== false) {
                 $nametype = trim(substr($part, 0, $pos));
                 $default = trim(substr($part, $pos + 1));
                 $parts = preg_split('/\\s+/', $nametype);
                 if (count($parts) != 2) {
                     throw new PC_PHPRef_Exception('Parameter description has not 2 parts: "' . $nametype . '"');
                 }
                 list($type, $name) = $parts;
             } else {
                 // detect variable arguments
                 if (strpos($part, '...') !== false) {
                     $param->set_first_vararg(true);
                     // sometimes there is a space bewteen $ and ...
                     $part = preg_replace('/\\$\\s+\\.\\.\\./', '$...', $part);
                 }
                 $parts = preg_split('/\\s+/', $part);
                 if (count($parts) != 2) {
                     throw new PC_PHPRef_Exception('Parameter description has not 2 parts: "' . $part . '"');
                 }
                 list($type, $name) = $parts;
             }
             // detect references
             if (substr($name, 0, 1) == '&') {
                 $param->set_reference(true);
                 $name = substr($name, 1);
             }
             $param->set_name(trim($name));
             $param->set_mtype(self::get_param_type($type, $default));
             $param->set_has_doc(true);
             $method->put_param($param);
         }
     }
     return array('func', $classname, $method);
 }