public function checkFileSlice(XRef_IProjectDatabase $db, $file_name, $file_slice)
 {
     foreach ($file_slice as $c) {
         list($class_name, $line_number) = $c;
         $lr = $db->lookupMethod($class_name, '__construct', $parent_only = true);
         // if the base class has constructor, and the constructor is not abstract
         // (it's valid to declare constructor in PHP's interfaces)
         if ($lr->code == XRef_LookupResult::FOUND && !is_null($lr->elements[0]->bodyStarts)) {
             // oops, child doesn't call parent's constructor
             $base_class_name = $lr->elements[0]->className;
             // check that the method itself doesn't call call_user_func()
             // if it does, that it can indirectly call the base class constructor
             // See Swiftmailer for examples
             $lr = $db->lookupMethod($class_name, '__construct');
             if ($lr && $lr->code == XRef_LookupResult::FOUND && ($lr->elements[0]->flags & XRef_ProjectDatabase::FLAG_CALLS_USER_FUNC) != 0) {
                 // notice - can't tell if the parent constructor is called or not
                 $error_descr = array('code' => self::E_CANT_TELL_IF_PARENT_CALLED, 'text' => $class_name, 'params' => array($class_name), 'location' => array($file_name, $line_number));
             } else {
                 // warning
                 $error_descr = array('code' => self::E_MISSED_CALL_TO_PARENT_CONSTRUCTOR, 'text' => $class_name, 'params' => array($class_name, $base_class_name), 'location' => array($file_name, $line_number));
             }
             $this->errors[$file_name][] = $error_descr;
         }
     }
 }
Example #2
0
 public function checkFileSlice(XRef_IProjectDatabase $db, $file_name, $file_slice)
 {
     $checked_for_functions = $file_slice["checked"];
     foreach ($file_slice["called"] as $s) {
         list($kind, $function_name, $extra, $line_number, $num_of_arguments) = $s;
         if ($kind == self::F_CLASS_METHOD) {
             $class_name = $extra;
             $lr = $db->lookupMethod($class_name, $function_name);
             if ($lr->code != XRef_LookupResult::FOUND) {
                 // don't report error here, CheckClassAccess will do
                 // missed methods and missed classes/base classes are covered by CheckClassAccess plugin
                 continue;
             }
         } elseif ($kind == self::F_CLASS_CONSTRUCTOR) {
             $class_name = $extra;
             // try to find php5 constructor
             $lr = $db->lookupMethod($class_name, '__construct');
             if ($lr->code != XRef_LookupResult::FOUND) {
                 // or try to find php4 constructor
                 $lr = $db->lookupMethod($class_name, $class_name);
             }
             if ($lr->code == XRef_LookupResult::FOUND) {
                 // ok, found, check it later
             } elseif ($lr->code == XRef_LookupResult::CLASS_MISSING) {
                 // hm, can't validate because class or base class is missing
                 continue;
             } else {
                 // no constructor found
                 // default constructor with 0 arguments will be called
                 if ($num_of_arguments != 0) {
                     $this->errors[$file_name][] = array('code' => self::E_DEFAULT_CONSTRUCTOR, 'text' => $class_name, 'params' => array($class_name), 'location' => array($file_name, $line_number));
                 }
                 continue;
             }
         } elseif ($kind == self::F_NOT_QUALIFIED_FUNC) {
             // that's tricky -
             // foo() can be
             //  1. current \namespace\foo()
             //  2. global foo()
             //  3. method of current class (self::foo()) without class prefix, error
             $lr = null;
             $namespace = $extra;
             if ($namespace) {
                 // 1. try to find \namespace\foo()
                 $lr = $db->lookupFunction("{$namespace}\\{$function_name}");
                 if ($lr->code == XRef_LookupResult::FOUND) {
                     $function_name = "{$namespace}\\{$function_name}";
                 }
             }
             if (!$lr || $lr->code != XRef_LookupResult::FOUND) {
                 // 2. try to find global foo()
                 $lr = $db->lookupFunction($function_name);
             }
             if ($lr->code != XRef_LookupResult::FOUND) {
                 // 3. try to find CurrentClass::foo()
                 $from_class = $s[5];
                 if ($from_class) {
                     $lr = $db->lookupMethod($from_class, $function_name);
                     if ($lr->code == XRef_LookupResult::FOUND) {
                         // oops, we did find it
                         // looks like error
                         $this->errors[$file_name][] = array('code' => self::E_UNQUALIFIED_METHOD, 'text' => $function_name, 'params' => array($function_name, $from_class), 'location' => array($file_name, $line_number));
                         continue;
                     }
                 }
             }
             if ($lr->code != XRef_LookupResult::FOUND) {
                 if (!isset($checked_for_functions[$function_name])) {
                     $this->errors[$file_name][] = array('code' => self::E_UNKNOWN_FUNCTION, 'text' => $function_name, 'params' => array($function_name), 'location' => array($file_name, $line_number));
                 }
                 continue;
             }
         } elseif ($kind == self::F_FULLY_QUALIFIED_FUNC) {
             $lr = $db->lookupFunction($function_name);
             if ($lr->code != XRef_LookupResult::FOUND) {
                 if (!isset($checked_for_functions[$function_name])) {
                     $this->errors[$file_name][] = array('code' => self::E_UNKNOWN_FUNCTION, 'text' => $function_name, 'params' => array($function_name), 'location' => array($file_name, $line_number));
                 }
                 continue;
             }
         } else {
             throw new Exception($kind);
         }
         // here check the number of arguments of found method/function
         if ($lr->code != XRef_LookupResult::FOUND) {
             throw new Exception($lr->code);
         }
         $f = $lr->elements[0];
         // if the number of arguments doesn't match the number of parameters,
         // (and the function called doesn't call func_get_args()
         if ($num_of_arguments != count($f->parameters) && ($f->flags & XRef_ProjectDatabase::FLAG_CALLS_GET_ARGS) == 0) {
             $min_number_of_arguments = 0;
             foreach ($f->parameters as $p) {
                 if ($p->hasDefaultValue || $p->name == '...') {
                     break;
                 }
                 $min_number_of_arguments++;
             }
             $last_parameter = end($f->parameters);
             $max_number_of_arguments = $last_parameter && $last_parameter->name == '...' ? self::ELLIPSES_ARGS : count($f->parameters);
             if ($num_of_arguments < $min_number_of_arguments || $num_of_arguments > $max_number_of_arguments) {
                 if ($min_number_of_arguments == $max_number_of_arguments) {
                     $arg_str = $min_number_of_arguments;
                 } elseif ($max_number_of_arguments == self::ELLIPSES_ARGS) {
                     $arg_str = $min_number_of_arguments . "..n";
                 } else {
                     $arg_str = $min_number_of_arguments . ".." . $max_number_of_arguments;
                 }
                 if ($kind == self::F_CLASS_CONSTRUCTOR) {
                     $this->errors[$file_name][] = array('code' => self::E_WRONG_CONSTRUCTOR_ARGS, 'text' => $class_name, 'params' => array($class_name, $num_of_arguments, $arg_str), 'location' => array($file_name, $line_number));
                 } else {
                     $this->errors[$file_name][] = array('code' => self::E_WRONG_NUMBER_OF_ARGS, 'text' => $function_name, 'params' => array($function_name, $num_of_arguments, $arg_str), 'location' => array($file_name, $line_number));
                 }
             }
         }
     }
 }
Example #3
0
 private function checkAccessError(XRef_IProjectDatabase $db, $class_name, $key, $name, $from_class, $is_static, $check_parent_only)
 {
     /** @var $lr XRef_LookupResult */
     $lr = null;
     switch ($key) {
         case 'property':
             $lr = $db->lookupProperty($class_name, $name, $check_parent_only);
             break;
         case 'method':
             $lr = $db->lookupMethod($class_name, $name, $check_parent_only);
             break;
         case 'constant':
             $lr = $db->lookupConstant($class_name, $name, $check_parent_only);
             break;
     }
     if (!$lr || $lr->code == XRef_LookupResult::NOT_FOUND) {
         // definition not found
         if ($key == 'property') {
             $lr_magic = $db->lookupMethod($class_name, '__get', $check_parent_only);
             if ($lr_magic->code == XRef_LookupResult::FOUND) {
                 // can't validate reference to (missing) property,
                 // because it or it's base class has method '__get'
                 return array(self::E_MAGIC_GETTER, $class_name, array($class_name));
             }
         }
         if ($key == 'method' && $name == '__construct') {
             // ok, php creates a default constructor
         } else {
             switch ($key) {
                 case 'method':
                     $error_code = self::E_ACCESS_TO_UNDEFINED_METHOD;
                     break;
                 case 'constant':
                     $error_code = self::E_ACCESS_TO_UNDEFINED_CONSTANT;
                     break;
                 case 'property':
                     $error_code = self::E_ACCESS_TO_UNDEFINED_PROPERTY;
                     break;
                 default:
                     throw new Exception($key);
             }
             $uniq = "{$from_class}/{$class_name}/{$key}/{$name}";
             return array($error_code, $uniq, array($name, $class_name));
         }
     } elseif ($lr->code == XRef_LookupResult::CLASS_MISSING) {
         // definition not found because definition of either class or its base class is missing
         $missing_class_name = $lr->missingClassName;
         if (!isset($this->ignore_missing_classes[strtolower($missing_class_name)])) {
             if (strtolower($missing_class_name) == strtolower($class_name)) {
                 // class definition is missing
                 return array(self::E_MISSING_CLASS, $class_name, array($class_name));
             } else {
                 // base class definition is missing
                 return array(self::E_MISSING_BASE_CLASS, $class_name, array($class_name, $missing_class_name));
             }
         }
     } else {
         // got definition, check access
         $attributes = $lr->elements[0]->attributes;
         $found_in_class = $lr->elements[0]->className;
         // 1. static vs. instance
         if ($key != 'constant' && !($key == 'method' && $name == '__construct')) {
             if ($is_static) {
                 if (!XRef::isStatic($attributes)) {
                     // reference to instance method or property as if they were static
                     if ($key == 'method' || $key == 'property') {
                         return array(self::E_ACCESS_INSTANCE_AS_STATIC, "{$from_class}/{$class_name}/{$key}/{$name}", array($name, $found_in_class));
                     } else {
                         throw new Exception($key);
                     }
                 }
             } else {
                 if ($key == 'property' && XRef::isStatic($attributes)) {
                     // reference to static property as if it were instance
                     return array(self::E_ACCESS_STATIC_AS_INSTANCE, "{$from_class}/{$class_name}/{$name}", array($name, $found_in_class));
                 }
             }
         }
         // 2. public, private, protected
         if (XRef::isPublic($attributes)) {
             // ok
         } elseif (XRef::isPrivate($attributes)) {
             if (!$from_class || strtolower($found_in_class) != strtolower($from_class)) {
                 // attempt to access a private member (method or property) of class $found_in_class
                 // from $class_name
                 return array(self::E_PRIVATE_MEMBER, "{$from_class}/{$class_name}/{$name}", array($name, $found_in_class));
             }
         } elseif (XRef::isProtected($attributes)) {
             if (!$from_class || !$this->isSubclassOf($from_class, $found_in_class)) {
                 return array(self::E_PROTECTED_MEMBER, "{$from_class}/{$class_name}/{$name}", array($name, $found_in_class));
             }
         } else {
             // shouldn't be here
             throw new Exception("Should be public? {$attributes}");
         }
         // 3. check that the called method is defined, not only declared.
         // however, allow to call declared methods from abstract classes and traits
         if ($key == 'method' && is_null($lr->elements[0]->bodyStarts)) {
             $lc = $db->lookupClass($from_class);
             if ($lc && $lc->code == XRef_LookupResult::FOUND && ($lc->elements[0]->isAbstract || $lc->elements[0]->kind == T_TRAIT)) {
                 // ok, allow to call abstract method
             } else {
                 $found_in_class = $lr->elements[0]->className;
                 return array(self::E_ACCESS_TO_UNDEFINED_METHOD, "{$from_class}/{$found_in_class}/{$name}", array($name, $found_in_class));
             }
         }
     }
     return;
 }