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; }